Function.prototype.myBind = function (Context) { var self = this; var args = [].slice.call(arguments, 1); returnfunction () { var bindArgs = [].slice.call(arguments); return self.apply(Context, args.concat(bindArgs)); }; };
思路其实很简单,就是将两个 arguments 进行合并。 本以为这样就结束了,但是 MDN 提到了 bind 的另外一个特点:绑定函数自动适应于使用 new 操作符去构造一个由目标函数创建的新实例。当一个绑定函数是用来构建一个值的,原来提供的 this 就会被忽略。不过提供的参数列表仍然会插入到构造函数调用时的参数列表之前。什么意思呢?用代码演示一下:
1 2 3 4 5 6 7 8 9 10 11 12
var foo = { name: "Jack", }; functionbar(age, goods) { console.log(this.name); console.log(age); console.log(goods); } bar.prototype.friends = "a"; var BindFoo = bar.bind(foo, 1); var bindFoo = new BindFoo("1"); // undefined 1 1 console.log(bindFoo.friends); // a
this.name 竟然输出 undefined,那是因为 new 后,BindFoo 中 this 的指向改变了,指向了 bindFoo,而 BindFoo 实际是 bar 函数,并且 bindFoo 没有 value 属性,所以就输出了 undefined,通过 instanceof 就可以看出来,bindFoo 是 BindFoo 的实例,也是 bar 的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Function.prototype.myBind = function (Context) { var self = this; var args = [].slice.call(arguments, 1); var cacheFn = function () {}; var bindFun = function () { var bindArgs = [].slice.call(arguments); return self.apply( thisinstanceof cacheFn ? this : Context, args.concat(bindArgs) ); }; cacheFn.prototype = this.prototype; bindFun.prototype = new cacheFn(); return bindFun; };
我们进行分步讲解: 1、为什么要判断 this instanceof bindFun? 之前也说到,当将 bind 返回后函数当做构造函数时,bindFoo 即是 BindFoo 的实例也是 bar 的实例,BindFoo 即为返回来的函数,在我们模拟的代码中就是 bindFun 这个函数,并且当 new 之后 this 指向的是实例,所以用 this instanceof bindFun 判断的实际就是函数前有没有 new 这个关键词。 2、为什么要继承 this 的原型? 这是为了继承 bar 原型上的属性。 最后一步,健壮模拟的 bind,判断传过来的 this 是否为函数,也是最终版:
Function.prototype.myBind = function (Context) { if (typeofthis !== "function") { thrownewError( "Function.prototype.bind - what is trying to be bound is not callable" ); } var self = this; var args = [].slice.call(arguments, 1); var cacheFn = function () {}; var bindFun = function () { var bindArgs = [].slice.call(arguments); return self.apply( thisinstanceof cacheFn ? this : Context, args.concat(bindArgs) ); }; cacheFn.prototype = this.prototype; bindFun.prototype = new cacheFn(); return bindFun; };