另一个基础函数
_bind .bind(function, object, arguments])
_.bind = function(func, context) {
var args, bound;
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));//【1】【2】
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {//【3】
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
ctor.prototype = null;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
【1】Function.prototype.apply Function.prototype.apply (thisArg, argArray)
apply 和 call 功能类似,只不过只接受两个参数,第二个参数 argArray 是一个数组,作为函数的参数传入。
【2】这一句的意思是,如果支持原生的bind() Function.prototype.bind (thisArg , arg1 , arg2, …), 就调用原生的,并把当前的参数传入,第一个参数是func,之后的参数依次传入。
slice = Array.prototype.slice.call Array.prototype.slice.call
call将指定函数Function作为thisArg对象的方法来调用,将参数args传递给Function,返回值为Function的返回值。
这句是取第一位以后的arguments,这个arguments并不是数组,但是有个length属性,所以能用Array.prototype.slice调用
具体到这里,伪代码可以简略解释如下:
Array.prototype.slice = function(start,end){
var result = new Array();
//注释部分是处理参数为负数的情况,可以掠过
//len = this.length;
//start = start < 0 ? max(len + start) : min(start,len);
//end = end ? len : end;
//end = end < 0 ? max((len + end),0) : min(end,len);
for(var i = start; i < end; i++){
result.push(this[i]);
}
return result;
}
这里与内部实现有出入,不要深究细节,明白意思就行了。总之就是将当前函数的agruments转成数组
【3】这段代码有点复杂
先上一个简单版本,绑定一个函数到一个对象上:
_.bind = function(func, obj) {
var aArgs = slice.call(arguments, 2);
return function() {
return func.apply(obj, args.concat(slice.call(arguments)));
}
};
功能就是把bind()时传入的参数与原来的参数合并,返回一个函数,更详细的解释这里有
更多阅读参考partial application, 还有 JavaScript currying
Github上根据这个Issue, 为了更像原生的bind(), 需要支持new一个被绑定的函数。 在MDN的建议下,Pull request #282让这段代码变成这么复杂了
_.bind = function(func, obj) {
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
var aArgs = slice.call(arguments, 2),
fNOP = function () {},
fBound = function () {
return func.apply(this instanceof fNOP ? this : obj, aArgs.concat(slice.call(arguments)));
};
fNOP.prototype = func.prototype;
fBound.prototype = new fNOP();
return fBound;
};
最终版本:
var ctor = function(){};
_.bind = function bind(func, context) {
var bound, args;
// 如果原生支持,就用原生的.bind()
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
// 如果没有传入function参数,抛出异常
if (!_.isFunction(func)) throw new TypeError;
// 支持绑定 function 和 context 后面的参数 (所以用 .slice(2))
args = slice.call(arguments, 2);
// 返回绑定后的函数
return bound = function() {
// 如果没有用new关键字,(this instanceof bound)就为假
// 此时为正常的调用 bound(),bound 函数中的 `this` 和 arguments
// 都已经绑定,传入参数,直接调用函数就可以了。
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
// 如果使用了new关键字,`new bound()`
// (this instanceof bound)为真,此时模拟函数的构造函数
// (JavaScript中通过 new 关键字方式调用的函数都被认为是构造函数。)
// 具体步骤就是:创建一个对象A,将A的prototype指向原函数的prototype
// 执行原函数
// 如果原函数没有显式的return一个对象,则隐式的返回A对象的实例
ctor.prototype = func.prototype;
// ctor是空函数,不进行任何操作
// 只创建一个实例对象
var self = new ctor;
// 用这个新创建的实例作为 `this` 调用原函数,并传入相应的参数
// 原函数作为新实例的构造函数执行
var result = func.apply(self, args.concat(slice.call(arguments)));
// 执行的结果是 object 就返回,不是的话返回这个实例对象
// 因为标准规定 `new xxx` 操作必须返回对象
if (Object(result) === result) return result;
return self;
};
};
扩展阅读:
underscore functions 测试用例
Understanding the code of _.bind
underscore中的function类函数解析
深入理解JavaScript系列(2):揭秘命名函数表达式
深入理解JavaScript系列(5):强大的原型和原型链
JavaScript 秘密花园 构造函数
深入理解JavaScript系列(26):设计模式之构造函数模式
深入理解JavaScript系列(18):面向对象编程之ECMAScript实现
updated:2013年1月6日 19:03:00 星期日