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