JS 的 call apply bind 方法

js的call apply bind 方法都很常見,目的都是爲了改變某個方法的執行環境(context)數組

 

call瀏覽器

call([thisObj[,arg1[, arg2[,   [,.argN]]]]])app

thisObj
可選項。將被用做當前對象的對象。
arg1, arg2, argN ..
可選項。將被傳遞方法參數序列。函數

 

若是沒設置嚴格模式 「use strict」this

  當thisObj 不存在或 爲 undefined 或爲 null 或爲 this 時,則隱式地指向 全局對象(在瀏覽器中即爲 window)spa

第二個參數是一個個值prototype

 

applycode

apply([thisObj[,arg1, arg2, argN]])對象

apply和call相似,區別只是第二個參數,是一個數組(或類數組)的形式blog

 

bind

bind(thisArg [, arg1 [, arg2, …]]);

bind 也是改變某個方法的執行環境,區別也在於第二個參數(也是一個個的參數形式)和「返回值」的特性。

  它將一個func綁定給thisArg的上下文,並傳入相應的參數,並以一個新函數的形式返回,以供調用。

 

如 func.call(func1,var1,var2,var3)

對應的apply寫法爲:func.apply(func1,[var1,var2,var3])

對應的bind寫法爲: func.bind(func1,var1,var2,var3)() 

 

來舉個栗子:

//'use strict'

var name = 'name1';
var obj = {
    name: 'name2',
    sayName: function(str1,str2){
        str1 = str1 || '';
        str2 = str2 || '';
        console.log(str1 + this.name + str2);
    }
};

obj.sayName();

obj.sayName.bind(window,'Hello: ',' !')();

obj.sayName.apply(this,['hello: ',' ,']);

obj.sayName.call(obj,'hello: ',' .');

將會輸出:

 

注1:但IE9(包括IE9)以上的才支持bind

因此,在不支持bind的瀏覽器上,咱們須要模擬一下

Function.prototype.Bind = function(context){
    var self = this,
                // 獲取到bind第二個參數(中的全部參數)
        args = Array.prototype.slice.call(arguments,1);
        // 返回一個新的函數
    return function(){
        // 將相關參數賦給這個bind所在方法,並將執環境賦給context
        return self.apply(context,args);
    };
};

注2:

Function.prototype的apply和call是在1999年發佈的ECMA262 Edition3中才加入的(1998年發佈ECMA262 Edition2)。

在此前的的瀏覽器如IE5.01(JScript 5.0)中是沒有apply和call的。所以也會帶來一些兼容性問題。因此,

call的模擬:

Function.prototype.Call = function(context){
       // 首先判斷所給的context,即call的第一個參數
    context = (context == undefined) ? window : context;
    var temp = [],
        evalStr = '';
        // 最後要造成 一個eval字符串函數調用形式,以供動態執行
    for(var i=1,j=arguments.length; i<j; i++){
        temp.push('arguments[' + i + ']');
    }
        // 給context新增一個方法(擁有this值)
    context._apply = this;
    evalStr = 'context._apply(' + temp.join(',') + ')';
    // console.log(evalStr);
    try{
               // 執行函數調用
        eval(evalStr);
    }catch(e){
        throw new Error(e.message);
    }finally{
               // 銷燬該屬性
        delete obj._apply;
    }
};

apply的模擬:

apply也相似,由於第二個參數是類數組的形式,因此也要變換爲數組

// 第二個參數 args是爲了方便使用
Function.prototype.Apply = function(context,args){
    context = (context == undefined) ? window : context;
    var temp = [],
        evalStr = '';
        // 直接拿第二個參數數組的各個元素再進行組合join(',')
        // 爲何不直接用 arguments[1]呢?
        // 由於此時join也要用到 Array.prototype.join.call ,call又不必定支持
    for(var i=0,j=args.length; i<j; i++){
        temp.push('args[' + i + ']');
    }
    
    context._apply = this;
    evalStr = 'context._apply(' + temp.join(',') + ')';
    // console.log(evalStr);
    try{
        eval(evalStr);
    }catch(e){
        throw new Error(e.message);
    }finally{
        delete obj._apply;
    }
};

 

ok 來看一下對比效果

Function.prototype.Bind = function(context){
    var self = this,
        args = Array.prototype.slice.call(arguments,1);
    return function(){
        return self.apply(context,args);
    };
};

Function.prototype.Call = function(context){
    context = (context == undefined) ? window : context;
    var temp = [],
        evalStr = '';
    for(var i=1,j=arguments.length; i<j; i++){
        temp.push('arguments[' + i + ']');
    }
    context._apply = this;
    evalStr = 'context._apply(' + temp.join(',') + ')';
    console.log(evalStr);
    try{
        eval(evalStr);
    }catch(e){
        throw new Error(e.message);
    }finally{
        delete obj._apply;
    }
};

Function.prototype.Apply = function(context,args){
    context = (context == undefined) ? window : context;
    var temp = [],
        evalStr = '';
    for(var i=0,j=args.length; i<j; i++){
        temp.push('args[' + i + ']');
    }
    
    context._apply = this;
    evalStr = 'context._apply(' + temp.join(',') + ')';
    console.log(evalStr);
    try{
        eval(evalStr);
    }catch(e){
        throw new Error(e.message);
    }finally{
        delete obj._apply;
    }
};



var name = 'name1';
var obj = {
    name: 'name2',
    sayName: function(str1,str2){
        str1 = str1 || '';
        str2 = str2 || '';
        console.log(str1 + this.name + str2);
    }
};

obj.sayName();

obj.sayName.bind(window,'Hello: ',' !')();
obj.sayName.Bind(window,'Hello: ',' !')();

obj.sayName.apply(this,['hello: ',' ,']);
obj.sayName.Apply(this,['hello: ',' ,']);

obj.sayName.call(obj,'hello: ',' .');
obj.sayName.Call(obj,'hello: ',' .');

相關文章
相關標籤/搜索