call、apply、bind方法詳解

1. 每個函數都包含兩個非繼承而來的方法:call()方法和apply()方法。
2. 相同點:這兩個方法的作用是一樣的,只是傳參方式不一樣而已。

call()方法使用:

        window.msg = '1';
        document.msg = '2';

        var log = {msg: '3' };
        function change(){
            console.log(this.msg);
        }

        change.apply();         //1 
        change.apply(window);   //1
        change.apply(document); //2
        change.apply(this);     //1
        change.apply(log);       //3

        var Pet = {
          words : '...',
          speak : function (say) {
            console.log(say + ''+ this.words)
          }
        }
        Pet.speak('Speak'); // 結果:Speak...

        var Dog = {
            words:'Wang'
        }

        //將this的指向改變成了Dog
        Pet.speak.call(Dog, 'Speak'); //結果: SpeakWang

apply()方法使用示例 

 window.number = 'one';
 document.number = 'two';
 var s1 = {number: 'three' };
 function changeColor(){
     console.log(this.number);
 }

 changeColor.apply();         //one (默認傳參)
 changeColor.apply(window);   //one
 changeColor.apply(document); //two
 changeColor.apply(this);     //one
 changeColor.apply(s1);       //three


function Pet(words){
 this.words = words;
 this.speak = function () {
     console.log( this.words)
 }
}
function Dog(words){
 //Pet.call(this, words); //結果: Wang
Pet.apply(this, arguments); //結果: Wang
}
var dog = new Dog('Wang');
dog.speak();

apply()方法 接收兩個參數,一個是函數運行的作用域(this),另一個是參數數組。

語法:apply([thisObj [,argArray] ]);,調用一個對象的一個方法,2另一個對象替換當前對象。

說明:如果argArray不是一個有效數組或不是arguments對象,那麼將導致一個

TypeError,如果沒有提供argArray和thisObj任何一個參數,那麼Global對象將用作thisObj。

call()方法 第一個參數和apply()方法的一樣,但是傳遞給函數的參數必須列舉出來。

語法:call([thisObject[,arg1 [,arg2 [,...,argn]]]]);,應用某一對象的一個方法,用另一個對象替換當前對象。

說明: call方法可以用來代替另一個對象調用一個方法,call方法可以將一個函數的對象上下文從初始的上下文改變爲thisObj指定的新對象,如果沒有提供thisObj參數,那麼Global對象被用於thisObj。

 

  function add(c,d){
        return this.a + this.b + c + d;
    }

    var s = {a:1, b:2};
    console.log(add.call(s,3,4)); // 1+2+3+4 = 10
    console.log(add.apply(s,[5,6])); // 1+2+5+6 = 14 
        window.firstName = "Cynthia"; 
        window.lastName = "_xie";

        var myObject = {firstName:'my', lastName:'Object'};

        function getName(){
            console.log(this.firstName + this.lastName);
        }

        function getMessage(sex,age){
            console.log(this.firstName + this.lastName + " 性別: " + sex + " age: " + age 
        );
        }

        getName.call(window); // Cynthia_xie
        getName.call(myObject); // myObject

        getName.apply(window); // Cynthia_xie
        getName.apply(myObject);// myObject

        getMessage.call(window,"女",21); //Cynthia_xie 性別: 女 age: 21
        getMessage.apply(window,["女",21]); // Cynthia_xie 性別: 女 age: 21

        getMessage.call(myObject,"未知",22); //myObject 性別: 未知 age: 22
        getMessage.apply(myObject,["未知",22]); // myObject 性別: 未知 age: 22

 bind()最簡單的用法是創建一個函數,使這個函數不論怎麼調用都有同樣的this值。常見的錯誤就像上面的例子一樣,將方法從對象中拿出來,然後調用,並且希望this指向原來的對象。如果不做特殊處理,一般會丟失原來的對象。使用bind()方法能夠很漂亮的解決這個問題:

this.num = 9; 
var mymodule = {
  num: 81,
  getNum: function() { 
    console.log(this.num);
  }
};

mymodule.getNum(); // 81

var getNum = mymodule.getNum;
getNum(); // 9, 因爲在這個例子中,"this"指向全局對象

var boundGetNum = getNum.bind(mymodule);
boundGetNum(); // 81

bind() 方法與 apply 和 call 很相似,也是可以改變函數體內 this 的指向。

bind()方法會創建一個新函數,稱爲綁定函數,當調用這個綁定函數時,綁定函數會以創建它時傳入 bind()方法的第一個參數作爲 this,傳入 bind() 方法的第二個以及以後的參數加上綁定函數運行時本身的參數按照順序作爲原函數的參數來調用原函數。

直接來看看具體如何使用,在常見的單體模式中,通常我們會使用 _this , that , self 等保存 this ,這樣我們可以在改變了上下文之後繼續引用到它。 像這樣:

var foo = {
    bar : 1,
    eventBind: function(){
        var _this = this;
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(_this.bar);     //1
        });
    }
}

由於 Javascript 特有的機制,上下文環境在 eventBind:function(){ } 過渡到 $('.someClass').on('click',function(event) { }) 發生了改變,上述使用變量保存 this 這些方式都是有用的,也沒有什麼問題。當然使用 bind() 可以更加優雅的解決這個問題:

var foo = {
    bar : 1,
    eventBind: function(){
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(this.bar);      //1
        }.bind(this));
    }
}

在上述代碼裏,bind() 創建了一個函數,當這個click事件綁定在被調用的時候,它的 this 關鍵詞會被設置成被傳入的值(這裏指調用bind()時傳入的參數)。因此,這裏我們傳入想要的上下文 this(其實就是 foo ),到 bind() 函數中。然後,當回調函數被執行的時候, this 便指向 foo 對象。再來一個簡單的栗子:

var bar = function(){
console.log(this.x);
}
var foo = {
x:3
}
bar(); // undefined
var func = bar.bind(foo);
func(); // 3

這裏我們創建了一個新的函數 func,當使用 bind() 創建一個綁定函數之後,它被執行的時候,它的 this 會被設置成 foo , 而不是像我們調用 bar() 時的全局作用域。

apply、call、bind比較

var obj = {
    x: 81,
};
 
var foo = {
    getX: function() {
        return this.x;
    }
}
 
console.log(foo.getX.bind(obj)());  //81
console.log(foo.getX.call(obj));    //81
console.log(foo.getX.apply(obj));   //81

三個輸出的都是81,但是注意看使用 bind() 方法的,他後面多了對括號。

也就是說,區別是,當你希望改變上下文環境之後並非立即執行,而是回調執行的時候,使用 bind() 方法。而 apply/call 則會立即執行函數。

再總結一下:

  • apply 、 call 、bind 三者都是用來改變函數的this對象的指向的;
  • apply 、 call 、bind 三者第一個參數都是this要指向的對象,也就是想指定的上下文;
  • apply 、 call 、bind 三者都可以利用後續參數傳參;
  • bind 是返回對應函數,便於稍後調用;apply 、call 則是立即調用 。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章