JavaScript中的call 和apply的用途以及區別

apply 接受兩個參數,第一個參數指定了函數體內this 對象的指向,第二個參數爲一個帶下
標的集合,這個集合能夠爲數組,也能夠爲類數組,apply 方法把這個集合中的元素做爲參數傳
遞給被調用的函數:數組

var func = function( a, b, c ){
    alert ( [ a, b, c ] ); // 輸出 [ 1, 2, 3 ]
};
func.apply( null, [ 1, 2, 3 ] );

在這段代碼中,參數 一、二、3 被放在數組中一塊兒傳入func 函數,它們分別對應func 參數列
表中的a、b、c。瀏覽器

call 傳入的參數數量不固定,跟apply 相同的是,第一個參數也是表明函數體內的this 指向,
從第二個參數開始日後,每一個參數被依次傳入函數:app

var func = function( a, b, c ){
    alert ( [ a, b, c ] ); // 輸出 [ 1, 2, 3 ]
};
func.call( null, 1, 2, 3 );

當調用一個函數時,JavaScript 的解釋器並不會計較形參和實參在數量、類型以及順序上的函數

區別,JavaScript 的參數在內部就是用一個數組來表示的。從這個意義上說,apply 比call 的使用this

率更高,咱們沒必要關心具體有多少參數被傳入函數,只要用apply 一股腦地推過去就能夠了。spa

call 是包裝在apply 上面的一顆語法糖,若是咱們明確地知道函數接受多少個參數,並且想prototype

一目瞭然地表達形參和實參的對應關係,那麼也能夠用call 來傳送參數。code

call和apply的用途對象

1. 改變this 指向
call 和apply 最多見的用途是改變函數內部的this 指向,咱們來看個例子:blog

var obj1 = {
    name: 'sven'
};
var obj2 = {
    name: 'anne'
};
window.name = 'window';
var getName = function(){
    alert ( this.name );
};
getName(); // 輸出: window
getName.call( obj1 ); // 輸出: sven
getName.call( obj2 ); // 輸出: anne

當執行getName.call( obj1 )這句代碼時,getName 函數體內的this 就指向obj1 對象,因此
此處的

var getName = function(){
alert ( this.name );
};

實際上至關於:
var getName = function(){
alert ( obj1.name ); // 輸出: sven
};

在實際開發中,常常會遇到this 指向被不經意改變的場景,好比有一個div 節點,div 節點
的onclick 事件中的this 原本是指向這個div 的:

document.getElementById( 'div1' ).onclick = function(){
    alert( this.id ); // 輸出:div1
};

假如該事件函數中有一個內部函數func,在事件內部調用func 函數時,func 函數體內的this
就指向了window,而不是咱們預期的div,見以下代碼:

document.getElementById( 'div1' ).onclick = function(){
    alert( this.id ); // 輸出:div1
    var func = function(){
        alert ( this.id ); // 輸出:undefined
    }
    func();
};

這時候咱們用call 來修正func 函數內的this,使其依然指向div:

document.getElementById( 'div1' ).onclick = function(){
    var func = function(){
       alert ( this.id ); // 輸出:div1
    }
    func.call( this );
};

2. Function.prototype.bind

大部分高級瀏覽器都實現了內置的Function.prototype.bind,用來指定函數內部的this 指向,
即便沒有原生的Function.prototype.bind 實現,咱們來模擬一個也不是難事,代碼以下:

Function.prototype.bind = function( context ){
var self = this; // 保存原函數
return function(){ // 返回一個新的函數
        return self.apply( context, arguments ); // 執行新的函數的時候,會     把以前傳入的context
    // 看成新函數體內的this
    }
};
var obj = {
    name: 'sven'
};
var func = function(){
    alert ( this.name ); // 輸出:sven
}.bind( obj);
func();

咱們經過Function.prototype.bind 來「包裝」func 函數,而且傳入一個對象context 看成參
數,這個context 對象就是咱們想修正的this 對象。

在Function.prototype.bind 的內部實現中,咱們先把func 函數的引用保存起來,而後返回一
個新的函數。當咱們在未來執行func 函數時,實際上先執行的是這個剛剛返回的新函數。在新
函數內部,self.apply( context, arguments )這句代碼纔是執行原來的func 函數,而且指定context
對象爲func 函數體內的this。

這是一個簡化版的Function.prototype.bind 實現,一般咱們還會把它實現得稍微複雜一點,
使得能夠往func 函數中預先填入一些參數:

Function.prototype.bind = function(){
    var self = this, // 保存原函數
    context = [].shift.call( arguments ), // 須要綁定的this 上下文
    args = [].slice.call( arguments ); // 剩餘的參數轉成數組
    return function(){ // 返回一個新的函數
        return self.apply( context, [].concat.call( args, [].slice.call(    arguments ) ) );
        // 執行新的函數的時候,會把以前傳入的context 看成新函數體內的this
       // 而且組合兩次分別傳入的參數,做爲新函數的參數
    }
};
var obj = {
    name: 'sven'
};
var func = function( a, b, c, d ){
    alert ( this.name ); // 輸出:sven
    alert ( [ a, b, c, d ] ) // 輸出:[ 1, 2, 3, 4 ]
}.bind( obj, 1, 2 );
func( 3, 4 );
相關文章
相關標籤/搜索