前言
前些天看到遇到Function.apply.bind(foo,null)这么一行代码,冥思苦想而不得,终于今日下了决心把它解决掉。
内容
一、要理解这行代码,当然首先得理解apply和bind的相关特性了。
apply:调用一个函数, 其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数。详情见这里
bind:函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当新函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。详情见这里
二、模拟原生的bind实现代码如下
//最接近原生的bind实现方法,基于此进行分析
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind");
}
//debugger;
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
//debugger;
return fToBind.apply(
this instanceof fNOP ? this : oThis,
// 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
// 此时aArgs=[null]
aArgs.concat(Array.prototype.slice.call(arguments))
);
};
// 维护原型关系
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};
三、用例及代码解析
function foo(x, y) {
console.log(x, y);
}
function spread(fn) {
return Function.apply.bind(fn, null);
}
spread(foo)([3, 10]);
//代码解析:
/*
spread(foo)([3, 10]) ==>
Function.apply.bind(foo,null)([3, 10]) ==>
function() {
//此时aArgs = [null]
return apply.apply(foo,aArgs.concat(Array.prototype.slice.call(arguments)));
}([3, 10]) ==>
//此时第一个apply是作为业务方法,第二作为apply工具方法,因此会把参数解构成列表形式
return apply.apply(foo,[null,3,10]) ==>
//根据apply方法语义(指定为函数指定执行this)得到等价执行代码
return foo.apply(null,3,10) ==>
//当apply指定的this为null时,非严格模式下会自动指向全局对象
foo(3,10)//3,10
*/
结束语
有不对的地方请诸位同仁指正。