# 0325
## Promise 再戰
Promise 會接兩個參數, 成功跟失敗的函數
```jsx
function abc(ok, ng){ // 預期這兩個參數是函數
ok(888) // 此處先假設成功, 失敗先不理
ng(1111)
}
const p1 = new Promise(abc); //新建一個自創的 promise 物件
console.log(p1) //回傳該promise
p1.then(() => { //then 是成功的回傳
console.log(123)
console.log(x) //會印出 123 跟 888
}).catch((y)=>{
console.log(y) // catch 擺前面會再次呼叫then並將 undefinded 給他,並將結果回傳
})
```
詳情請見 MDN
## [常規表達式 Regular Expression REGEX](https://regex101.com/)
[MDN](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Regular_expressions)
可以用變數定義, 並用 / 來定義規則 ; 或是變數定義加 new
```jsx
// 方法一
let re = /ab+c/
// 方法二
let re = new RegExp("ab+c")
```
有些字有特殊意義, 可用 \ 反斜線來將其功能取消
```jsx
const someStr = 'I\'m a robot' // I'm a robot 縮寫記號可保留在字串內
```
### match
可用 match 來判斷有沒有符合條件, 符合的話會回傳陣列, 不然就是空值
可在用 boolean() 來轉成布林值, 或是用 ! 轉成 true/false 再依需求在可慮是否再加一個 !
## String(hero) == hero.toString()
String(), 會呼叫hero 的 toString() 方法
幫忙呼叫與自己呼叫(toString)的差別
## THIS
規則
1. 誰呼叫, 誰就是 this, 無人呼叫就是 global (瀏覽器為window/ NODE為 object)
- 跟寫在哪裡無關, 只跟誰呼叫有關
- abc.fn() 就是指abc呼叫, 若只有 fn() 就是無人呼叫
2. 是否使用箭頭函式, 箭頭函式沒有 this, 如果呼叫的物件為箭頭函示也會往外找
3. 使否有使用 new ,有 new 會得到回傳的空物件
4. 是否有用 apply, call, bind
5. 是否有開啟 strict mode
其他, this 必須是個物件, 不符合會幫忙包起來變成物件形式
### 規則一補充
```jsx
function cc(){
function aa(){
console.log(this)
}
aa() //如果沒這個, 什麼都不會發生
}
cc() // 雖然會執行 aa(), 但沒人呼叫他, 故 this 是全域物件
```
### 規則二補充
```jsx
const hero = {
name: ‘kk’,
a: ( ) => {
console.log(this)
} ,
b: function ( ){
console.log(this)
}
}
hero.a // 全域
hero.b // hero
```
箭頭函數本身沒有 this 所以會往外面找
```jsx
const hero = {
name: 'kk',
sayMyName: () =>{
console.log(this.name) //這邊的 this 會是全域, 要改用普通的 function 定義才可以
}
}
hero.sayMyName()
```
### 規則三補充
new 會在第一部建立新的空物件, 並在最後賦值, 故最後印出的是 新物件
```jsx
function a(){
console.log(this)
}
new a() // 會印出 a{}
```
### 規則四補充
可使用 .call(), .apply(), .bind(), 來改變 this 的指向
- call : 第一個引數不是參數, 而是 this 的指向, 若為多參數則繼續寫, 如:some.call(this指向誰, 引數1, 引數2, 引數3 …)
- apply : 與 call 差不多, 兩個參數, 第一個為 this 的指向, 第二個為陣列 (不管裡面有多少個值), 如:some.apply(this指向誰, [引數1, 引數2, …])
```jsx
function a(){
console.log(this.name)
}
const h = {name : 'kk'}
a.call(h) // kk
```
```jsx
// 遊戲做範例,補師補戰士
const warrior = {
hp: 100,
attack: function(){console.log('att')}
}
const healer = {
hp: 70,
heal: function(){this.hp += 30}
}
healer.heal() //正常為自己呼叫自己所擁有的 Fn,並指向自己
healer.heal.apply(warrior) //這樣就可以改變 this, 去指向warrior
```
```jsx
function a() {
console.log(this)
}
a.apply([]) // [] <- 因指向空陣列
a.apply(123) // {123} <- 包起來的123(轉成數字物件), 其型別還是數字, 值123
a.apply(“aaa") //{'aaa'} <-包起來的'aaa'(轉成字串物件)
a.apply({}) // {}
```
- bind : 回傳新的 function, 所以 f 會是一個 function, 此例為 f = a() 但 a 的 this 會變成 h , 他是回傳一個 function 所以不會馬上發動, 第一個參數也是給 this 指向, 此函數危淺拷貝, 如下例 h 的 name 改變 f() 所印出的也會改變
- 局部函數, partial function : 可以晚點在給參數 ; bind 所產生的函式為局部函式
```jsx
function a(){
console.log(this)
}
const h = {name : 'kk'}
const f = a.bind(h) //此時得到 this 指向 h 並繼承 a() 的 f(), 故下一行可執行f
console.log(f) // a(){console.log(this)}
f() // {name : 'kk'}
```
```jsx
// 局部函數 ; bind 所產生的函式就此特性
function add(a, b){
return a + b
}
const add5 = add.bind(null, 5); //沒有this指向的問題, 故第一個參數為null, 並先給 a,
//此時 add5 會像是 add5(b){return 5 + b}
console.log(add5(10)) // 15
console.log(add5(4)) // 9
```
### 規則五補充
使用 model, 或 “use strict” 會開啟嚴格模式
```jsx
“use strict”
function a(){
cconsole.log(this)
}
a() // 此時所印出的是 undefined
```
### 其他補充
```jsx
//如果在生成函數裡面使用箭頭函數, 此時 this 會壞掉
const a() => {
//第一步 this -> {} , 但無法執行該步驟
console.log(this) //箭頭函數沒有 this 所以 new 生成時沒東西可以指向, 所以無法生成新物件
}
new a();
```
## 閉包 clousure
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./clousuretest.js" defer></script>
</head>
<body>
<button class="btn">red</button>
<button class="btn">blue</button>
<button class="btn">green</button>
</body>
</html>
```
```jsx
const btn = document.querySelectorAll(".btn");
btn.forEach((btn) => {
btn.addEventListener("click", () => { // 此時沒有 this (箭頭函式), 故會印出全域
console.log(this);
});
});
// 若以function改寫
btn.forEach((btn) => {
btn.addEventListener("click", function(){ // 可順利印出 btn
console.log(this);
});
});
```
閉包例
```jsx
const btn = document.querySelectorAll(".btn");
btn.forEach((btn) => {
btn.addEventListener("click", function () {
setTimeout(() => {
console.log(this); //此時會印出 btn
}, 500);
});
});
// 上方類似 Scope(作用域)的機制,看程式碼定義在哪裡,就會利用定義的 block 來呈現這個 this 的值。
// [出處](https://hackmd.io/@Heidi-Liu/note-js201-this)
btn.forEach((btn) => {
btn.addEventListener("click", function () {
setTimeout(function (){
console.log(this); //此時會印出全域
}, 500);
});
});
// 因上例會使 this 發生改變, 故有另一做法是先將該 this 用 用變數存起來
btn.forEach((btn) => {
btn.addEventListener("click", function () {
var that = this
//此處的 that 為閉包, 印出時的 this 不同, 故會先判斷把目前的 this 存留起來
setTimeout(function (){
console.log(that); //此時會印出 btn
}, 500);
});
});
```
```jsx
btn.forEach((btn) => {
btn.addEventListener("click", function () {
const handler = function(){
console.log(this);
}
setTimeout(handler.bind(this), 500); // 將函數寫在外面, 並用bind 讓this 有所指向
}); // 這邊指向 btn
});
```
```jsx
btn.forEach((btn) => {
btn.addEventListener("click", function () {
const handler = function(){console.log(this);}
handler() // 全域
handler.call(this) //btn
handler.bind(this) //回傳函式, 故沒發生任何事
});
});
```
other example
```jsx
for (var i = 0; i < 3; i++){
var x = i
setTimeout(() => {
console.log(x);
}, 0)
} // 2, 2, 2 (因為到 queue 時, 可抓到 x 故會以當時候的值去印出來)
for (var i = 0; i < 3; i++){
let x = i
setTimeout(() => {
console.log(x);
}, 0)
} // 0, 1, 2. (等到執行時 x 已經不一樣了, 故會以閉包跟著傳入
for (let i = 0; i < 3; i++){
setTimeout(() => {
console.log(i);
}, 0)
} // 0, 1, 2 (理由同上, 執行時已無 i 故會以閉包方式運作
```
## arguments
在 ES6 之前, 若有不定數的印數傳入, 會用 arguments 此變數代稱, arguments是物件, 如:
```jsx
const hi = function(){
console.log(arguments)
}
hi(1, 2, 3, 4, 5) // [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
```
但在箭頭函式裡沒有 arguments, 如:
```jsx
const hi = () => {
console.log(arguments)
}
hi(1, 2, 3, 4, 5) // arguments is not defined
```
## IIFE (Immediately Invoked Function Expression 立即調用函式)
定義函式後馬上使用的函式, 如:jQuery
好處是在裡面定義的變數只會在這裡面的 function 內存活, 故調用完後就不存在, 不會有污染到外面的情況發生
```jsx
((x, y) => {console.log(x + y)})(3, 5) // 8
```
## 額外衍生 ( jQuery 內拆陣列法)
```jsx
const arr = [1, 2, 3, [1, 1]]
console.log(arr.flat())
console.log(arr.concat.apply([],arr))
console.log([].flat.call(arr))
```
[因 concat 將陣列內的值回傳, 藉此特性來拆二維以上的陣列](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/concat)