1. 程式人生 > >深入理解 call,apply 和 bind

深入理解 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(){}

//定義函式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

            //p2

    )

}

當點選網頁時,onClick 被觸發執行,輸出onepixel p1 p2, 說明 onClick 中的 this 被 bind 改變成了obj 物件