---
# System prepended metadata

title: '0325'
tags: [五倍, JS]

---

# 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)

