比較call( ) apply( ) bind( )

簡單比較一下call() apply() bind()

《JavaScript: The Definitive Guide》web

call()與apply()

call()和apply()的第一個實參是要調用函數的母對象,它是調用上下文,在函數體內經過this來得到對它的引用。簡單來講就是把一個方法綁定到一個對象上去調用:編程

慄如,要想以對象o的方法來調用函數f():數組

f.call(o);
f.apply(o);

其實至關於:閉包

o.m = f; //將f存儲爲o的臨時方法
o.m(); //調用它,不傳入參數
delete o.m; //將臨時方法刪除

--> 對於call(),第一個實參以後的全部實參就是要傳入待調用函數的值。慄如:app

//以對象o的方法的形式調用函數f(),並傳入兩個參數
f.call(o, 1, 2);

--> 對於apply(),它的實參都放入一個數組當中:ide

f.apply(o,[1, 2]);

給apply()傳入的參數數組能夠是任意長度的,慄如:函數式編程

//找出一個數組中最大的數值元素
var biggest = Math.max.apply(Math, array_of_numbers);

傳入apply()的參數數組能夠是類數組對象也能夠是真實數組。實際上,能夠將當前函數的arguments數組直接傳入apply()來調用另外一個函數:函數

//將對象o中名爲m的方法替換爲另外一個方法
//能夠在調用原始方法以前和以後記錄日誌消息
function trace(o, m) {
    var original = o[m];//在閉包中保存原始方法
    o[m] = function() {//定義新的方法
        console.log(new Date(), 'Entering', m);
        var result = original.apply(this, arguments);//調用原始函數
        console.log(new Date(), 'Exiting', m);
        return result;
     };
}
//這個新方法是包裹原始方法的另外一個泛函數 (monkey-patching)?

bind() 方法

bind()是ES5中的方法。
當在函數f()上調用bind()方法並傳入一個對象o做爲參數,這個方法將返回一個新的函數,(以函數調用的方式)調用新的函數將會把原始的函數f()看成o的方法來調用。傳入新的函數的任何實參都講傳入原始函數。慄如:ui

function f(y) { return this.x + y; }
var o =  { x: 1 };
var g = f.bind(o);
g(2); //=> 3

能夠用ES3簡單模擬:this

function bind(f, o) {
    if(f.bind) return f.bind(o);
    else return function() {
        return f.apply(o, arguments);
    }
}

然而,bind()方法不單單是將函數綁定至一個對象----除了第一個參數外,傳入bind()的實參也會綁定至this,這個附帶的應用是一種常見的函數式編程技術,也被稱爲「柯里化「。

function f(y, z) { return this.x + y + z }
var g = f.bind({ x: 1 }, 2);
g(3);//=> 6 this.x綁定到1, y綁定到2, z綁定到3

若是用ES3模擬:

if(!Function.prototype.bind){
    Function.prototype.bind = function(o /*, args*/){
        //將this和arguments的值保存至變量中
        //以便在後面嵌套的函數中能夠使用它們
        var self = this, boundArgs = arguments;
        
        //bind方法的返回值是一個函數
        return function(){
            //建立一個實參列表,將傳入bind()的第二個及後續的實參傳入這個參數
            var args = [], i;
            for(i=1; i<boundArgs.length; i++){
                args.push(boundArgs[i];
            }
            for(i=0;i<arguments.length;i++){
                args.push(arguments[i];
            }
            //如今將self做爲o的方法來調用,傳入這些實參
            return self.apply(o, args);
        };
    };
}

http://web.jobbole.com/83642/
深刻淺出妙用 Javascript 中 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 則是當即調用 。

一道題:

定義一個 log 方法,讓它能夠代理 console.log 方法

function log(){
    console.log.apply(console, arguments);
}

若要給每個 log 消息添加一個」(app)」的前輟?

//該怎麼作比較優雅呢?這個時候須要想到arguments參數是個僞數組,經過 //Array.prototype.slice.call 轉化爲標準數組,再使用數組方法unshift
function log(){
    var args = Array.prototype.slice.call(arguments);
    args.unshift('(app)');
    console.log.apply(console, args);
}
相關文章
相關標籤/搜索