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: ',' .');