# Explain Function.prototype.bind. ## 一、名詞定義、概念 bind() 方法,javascript中function物件內建的method。 會建立一個新函式。該函式被呼叫時,會將 this 關鍵字設為給定的參數。 使用這個方法,並不需要給與對應的參數或參數陣列,然而他也不會回傳你函數執行結果,而是給你一個「綁定this的函數」,一但被綁定,其this無法再被修改。 寫React的時候,bind只會出現在class component的寫法。因為function component(hook)不會用到this,所以也不用bind。 ## 二、機制運作方式 ### 建立綁定函式 1. this 是動態特性:在全域環境下的this V.S 函式環境下的this (參考:版本二 Javascript this) 3. bind() 幫我們建立了一個新的 function ,並且當我們執行時這個 function 的 this 是指向 foo,而不是全域。 ``` var foo = { x: 3 } var bar = function () { console.log(this.x); } bar(); // undefined //this =>window //等同於 window.bar() var boundFunc = bar.bind(foo); //等同於 foo.bar() boundFunc(); // 3 ``` <hr/> ### 使用綁定函式 ### -參數 **function.bind(thisArg[, arg1[, arg2[, ...]]])** * **第一個參數(thisArg):** 1. 參數傳遞給目標函式的值 2. 任何傳遞值都會轉成物件 3. 當為空、null、undefined時,執行作用域的this將背是為新函數的thisArg * **第一個之後的參數(arg1, arg2, ...):** 1. 當目標函數被調用時,被預置入綁定函數的參數列表中的函數 ### -返回值 * 返回一個新的函式 * 該函式具有指定的this的值和初始參數 * bind() 被調用時,這个新函数的 this 被指定成 bind() 的第一個参数 * 其餘参数將作为新函数的参数,供調用時使用。 bind()方法会把传入它的第一个实参绑定给f函数体内的 this,从第二个实参起,将依此传递给原始函数,因此 {x:1}传递给this ,2传递给形参y,m(3) 调用时的3 传递给形参z。 ``` function f(y,z){ return this.x+y+z; } var m=f.bind({x:1},2) console.log(m(3)); // 6 ``` <hr/> ### Function.prototype.bind() 內部運行機制 * 創建新的函式-**綁定函式 bound function, BF** * 呼叫綁定函式會執行**包裝函式(wrapped function)**,包裝原函數對象(指向的物件),調用綁定函數 * 绑定函数具有以下内部属性 1. **TargetFunction:** 包裝函式的對象 2. **This:** 調用包裝函式時,作為this所傳遞的值 3. **Arguments:** 對於包裝函式作任何調用時,都會優先用列表內的元素,填充到參數列表中 4. **Call:** 與物件相關的執行代碼,透過表達式調用。包含this值和傳遞到函式的Arguments的列表 ![](https://i.imgur.com/saIqtx4.png) <hr/> <hr/> ## 補充:function 三個方法, bind()、apply()、call() | | bind | call | apply | | -------- | -------- | -------- |-------- | | 語法 | wtf.bind(obj)(1, 2); | wtf.call(obj, 1, 2); |wtf.apply(obj, [1, 2]); | 用法 | 使用給定的this參數,但不一定要給與對應的參數或參數陣列| 使用給定的this參數以及分別給定的參數來呼叫某個函數|只允許兩個變數傳入,第二個變數等於是原始function的參數陣列。 | 回傳 | 綁定 this 後的原函數 | function執行結果 |function執行結果 ``` function wtf(argv0, argv1) { console.log(this.v, argv0, argv1); } var obj = { v: 'I am v of obj' }; // We hope we can get the obj this // so assign function in obj obj.func = wtf; obj.func(1, 2); // I am v of obj 1 2 // or we just tell the function what `this` in first param wtf.apply(obj, [1, 2]); // I am v of obj 1 2 wtf.bind(obj)(1, 2); // I am v of obj 1 2 wtf.call(obj, 1, 2); // I am v of obj 1 2 ``` ## 三、案例說明 ### 使用bind的時機 以下情況產生無法取得this的問題: 實際執行我們可以發現 laterHello 的這個方法,他的 timer callback 拿不到正確的 this的問題。 ``` class dog{ constructor(name){ this.name = name; } sayHello(){ console.log('Hello I am ',this.name); } laterHello(){ setTimeout(function(){ console.log('(1 sec...) Hi!, I am',this.name) }, 1000); } } let boo = new dog('fongki'); boo.sayHello(); // Hello I am fongki boo.laterHello(); // (1 sec...) Hi!, I am undefined ``` #### 三種思路 * **Self大法駕到** 把 this 儲存成一個變數,接著即便切換Context 還是可以參考到這個物件。 使用閉包的概念: > 閉包: >1. 它是一個 function。 >2. 它產生了一個 Context ,概略的說就是幫你記錄上一層有宣告的變數。 ``` laterHello() { var self = this; // let keep the class's this setTimeout(function() { console.log('(1 sec...) Hi!, I am', self.name); // In the context, we can access `self` // so we could get `self.name` }, 1000); } ``` * **Binding大法** 透過創建綁定函式來解決 不需要改變回調函數本身的區塊,而是在外頭直接告訴callback該用哪個作為正確的this。 ``` laterHello() { setTimeout( function() { console.log('(1 sec...) Hi!, I am', this.name); }.bind(this), 1000 ); } ``` * **Arrow Function** 箭頭函數 多數時候一般函數無異,但是最大的差別在於 — 其 this 完全綁定在語彙上的位置,也就是說在 arrow 裡面的 this 永遠都是語意上的 this ,不管是誰呼叫他,或是被如何 bind 、 call 、 apply ,他永遠都是拿到原先作用域的 this 。 ``` laterHello() { setTimeout(() => { console.log('(1 sec...) Hi!, I am', this.name); }, 1000); } ``` ## 四、實際應用 [老師課程:React其他重要主題-事件處理](https://www.udemy.com/course/javascript-es6-react-redux/learn/lecture/9088514#overview) ``` class MyHead extends React.Component{ render(){ return ( //把組建實體bind到事件處理 <div onClick={this.clickHandler.bind(this)} className={'head-'+this.props.level} >Hello Component</div>; )} clickHandler(e){ console.log("觸發點擊事件"); console.log(this.props); //利用this取得當前組件,進一步取得props或state } } ``` [codepen 簡易應用1-onClick事件](https://codepen.io/beckyhung37/pen/abWMgeJ) [codepen 簡易應用2-onChange事件](https://codepen.io/philbaker/pen/XgGLVj) ## 五、參考來源 https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/bind https://ithelp.ithome.com.tw/articles/10155643 https://www.cnblogs.com/leoo2sk/archive/2010/12/19/ecmascript-scope.html#comment_tip https://realdennis.medium.com/javascript-%E8%81%8A%E8%81%8Acall-apply-bind%E7%9A%84%E5%B7%AE%E7%95%B0%E8%88%87%E7%9B%B8%E4%BC%BC%E4%B9%8B%E8%99%95-2f82a4b4dd66 https://realdennis.medium.com/javascript-%E8%81%8A%E8%81%8Acall-apply-bind%E7%9A%84%E5%B7%AE%E7%95%B0%E8%88%87%E7%9B%B8%E4%BC%BC%E4%B9%8B%E8%99%95-2f82a4b4dd66 https://stackoverflow.com/questions/53215067/how-can-i-bind-function-with-hooks-in-react