2.2-call與apply

來源:JavaScript設計模式與開發實踐

Function.prototype.call 和Function.prototype.apply的作用一模一樣,區別僅在於傳入參數形式的不同。

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

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

func.apply( null, [ 1, 2, 3 ] );

在這段代碼中,參數 1、2、3 被放在數組中一起傳入func 函數,它們分別對應func 參數列表中的a、b、c。

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


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

func.call( null, 1, 2, 3 );

當調用一個函數時,JavaScript 的解釋器並不會計較形參和實參在數量、類型以及順序上的區別,JavaScript 的參數在內部就是用一個數組來表示的。從這個意義上說,apply 比call 的使用更高,我們不必關心具體有多少參數被傳入函數,只要用apply 一股腦地推過去就可以了。

call 是包裝在apply 上面的一顆語法糖,如果我們明確地知道函數接受多少個參數,而且想一目瞭然地表達形參和實參的對應關係,那麼也可以用call 來傳送參數。

當使用call 或者apply 的時候,如果我們傳入的第一個參數爲null,函數體內的this 會指向默認的宿主對象,在瀏覽器中則是window:

var func = function( a, b, c ){
    alert ( this === window ); // 輸出true
};

func.apply( null, [ 1, 2, 3 ] );

//但如果是在嚴格模式下,函數體內的this 還是爲null:

var func = function( a, b, c ){
    "use strict";
    alert ( this === null ); // 輸出true
}

func.apply( null, [ 1, 2, 3 ] );

call和apply的用途

1. 改變this 指向

call 和apply 最常見的用途是改變函數內部的this 指向,我們來看個例子:

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 的,假如該事件函數中有一個內部函數func,在事件內部調用func 函數時,func 函數體內的this就指向了window,而不是我們預期的div。

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

    func.call( this );
};


e.g.2
document.getElementById = (function( func ){
    return function(){
        return func.apply( document, arguments );
    }
})( document.getElementById );

var getId = document.getElementById;
var div = getId( 'div1' );
alert ( div.id ); // 輸出: div1

2. 借用其他對象的方法

杜鵑既不會築巢,也不會孵雛,而是把自己的蛋寄託給雲雀等其他鳥類,讓它們代爲孵化和養育。同樣,在JavaScript 中也存在類似的借用現象。

借用方法的第一種場景是“借用構造函數”,通過這種技術,可以實現一些類似繼承的效果:

var A = function( name ){
    this.name = name;
};

var B = function(){
    A.apply( this, arguments );
};

B.prototype.getName = function(){
    return this.name;
};

var b = new B( 'sven' );
console.log( b.getName() ); // 輸出: 'sven'

借用方法的第二種運用場景

函數的參數列表arguments 是一個類數組對象,雖然它也有“下標”,但它並非真正的數組,所以也不能像數組一樣,進行排序操作或者往集合裏添加一個新的元素。這種情況下,我們常常會借用Array.prototype 對象上的方法。比如想往arguments 中添加一個新的元素,通常會借用

Array.prototype.push:

(function(){
    Array.prototype.push.call( arguments, 3 );
    console.log ( arguments ); // 輸出[1,2,3]
})( 1, 2 );

在操作arguments 的時候,我們經常非常頻繁地找Array.prototype 對象借用方法。

想把arguments 轉成真正的數組的時候,可以借用Array.prototype.slice 方法;想截去arguments 列表中的頭一個元素時,又可以借用Array.prototype.shift 方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章