深入理解 call,apply 和 bind
在JavaScript 中,call、apply 和 bind 是 Function 物件自帶的三個方法,這三個方法的主要作用是改變函式中的 this 指向,從而可以達到`接花移木`的效果。本文將對這三個方法進行詳細的講解,並列出幾個經典應用場景。
1、call(thisArgs [,args...])
該方法可以傳遞一個thisArgs引數和一個引數列表,thisArgs 指定了函式在執行期的呼叫者,也就是函式中的 this 物件,而引數列表會被傳入呼叫函式中。thisArgs 的取值有以下四種情況:
- 不傳,或者傳null,undefined, 函式中的 this 指向 window 物件
- 傳遞另一個函式的函式名,函式中的 this 指向這個函式的引用
- 傳遞字串、數值或布林型別等基礎型別,函式中的 this 指向其對應的包裝物件,如 String、Number、Boolean
- 傳遞一個物件,函式中的 this 指向這個物件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function a(){ console.log(this);//輸出函式a中的this物件 } function b(){} var obj = {name:'onepixel'};//定義物件obj a.call();//window a.call(null);//window a.call(undefined);//window a.call(1);//Number a.call('');//String a.call(true);//Boolean a.call(b);// function b(){} a.call(obj);//Object |
這是call 的核心功能,它允許你在一個物件上呼叫該物件沒有定義的方法,並且這個方法可以訪問該物件中的屬性,至於這樣做有什麼好處,我待會再講,我們先看一個簡單的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var a = { name:'onepixel',//定義a的屬性 say:function(){//定義a的方法 console.log("Hi,I'm function a!"); } }; function b(name){ console.log("Post params: "+ name); console.log("I'm "+this.name); this.say(); } b.call(a,'test'); >> Postparams: test I'm onepixel I'm function a! |
當執行b.call 時,字串`test`作為引數傳遞給了函式b,由於call的作用,函式b中的this指向了物件a, 因此相當於呼叫了物件a上的函式b,而實際上a中沒有定義b 。
2、apply(thisArgs [,args[]])
apply 和 call 的唯一區別是第二個引數的傳遞方式不同,apply 的第二個引數必須是一個數組(或者類陣列),而 call 允許傳遞一個引數列表。值得你注意的是,雖然 apply 接收的是一個引數陣列,但在傳遞給呼叫函式時,卻是以引數列表的形式傳遞,我們看個簡單的例子:
1 2 3 4 5 |
function b(x,y,z){ console.log(x,y,z); } b.apply(null,[1,2,3]);// 1 2 3 |
apply 的這個特性很重要,我們會在下面的應用場景中提到這個特性。
3、bind(thisArgs [,args...])
bind是ES5 新增的一個方法,它的傳參和call類似,但又和 call/apply 有著顯著的不同,即呼叫 call 或 apply 都會自動執行對應的函式,而 bind 不會執行對應的函式,只是返回了對函式的引用。粗略一看,bind 似乎比call/apply 要落後一些,那ES5為什麼還要引入bind 呢?
其實,ES5引入 bind 的真正目的是為了彌補 call/apply 的不足,由於 call/apply 會對目標函式自動執行,從而導致它無法在事件繫結函式中使用,因為事件繫結函式不需要我們手動執行,它是在事件被觸發時由JS 內部自動執行的。而 bind 在實現改變函式 this 的同時又不會自動執行目標函式,因此可以完美的解決上述問題,看一個例子就能明白:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var obj = {name:'onepixel'}; /** * 給document新增click事件監聽,並繫結onClick函式 * 通過bind方法設定onClick的this為obj,並傳遞引數p1,p2 */ document.addEventListener('click',onClick.bind(obj,'p1','p2'),false); //當點選網頁時觸發並執行 function onClick(a,b){ console.log( this.name,//onepixel a,//p1 b //p2 ) } |
當點選網頁時,onClick 被觸發執行,輸出onepixel p1 p2, 說明 onClick 中的 this 被 bind 改變成了obj 物件