解读Javascript中的Function.prototype.bind
首先贴上MSDN上标准的Polyfill:
1 if (!Function.prototype.bind) { 2 Function.prototype.bind = function(oThis) { 3 if (typeof this !== 'function') { 4 // closest thing possible to the ECMAScript 5 5 // internal IsCallable function 6 throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); 7 } 8 9 var aArgs = Array.prototype.slice.call(arguments, 1), 10 fToBind = this, 11 fNOP = function() {}, 12 fBound = function() { 13 return fToBind.apply(this instanceof fNOP 14 ? this 15 : oThis, 16 aArgs.concat(Array.prototype.slice.call(arguments))); 17 }; 18 19 if (this.prototype) { 20 // Function.prototype doesn't have a prototype property 21 fNOP.prototype = this.prototype; 22 } 23 fBound.prototype = new fNOP(); 24 25 return fBound; 26 }; 27 }
下面将上述代码逐行分析。
第一行判断Function的原型中是否有该函数, 因为bind函数并不是每个浏览器都支持,下面列出浏览器的支持情况:
第3行 if (typeof this !== 'function')用来判断调用bind函数的对象是否为函数,注意在整个bind函数作用域中this指代调用它的对象,且该对象必须为函数,通过typeof函数来检测。
第9行var aArgs = Array.prototype.slice.call(arguments, 1)中,arguments为传入bind函数的参数对象,由于其并不是数组,而是一种伪数组对象,但是可以使用数组的slice方法,由于bind函数的第一个参数为绑定的函数,故slice方法从1开始获取参数。
举例: someFunc.bind(oThis, arg1, arg2) ,则slice方法取到的就是[arg1, arg2]。
第12行fBound函数,为最终bind函数返回的新函数,通过apply方法将fToBind函数的上下文改为oThis。
第16行 aArgs.concat(Array.prototype.slice.call(arguments))),这行代码将bind函数中的参数和执行bind函数返回的函数传入的参数进行合并,最终传给实际调用的函数fToBind来使用。
第21行 fNOP.prototype = this.prototype; 将fToBind中的原型对象赋给一个没有原型的空函数,再通过fBound.prototype = new fNOP(); 进行原型函数继承,因为apply方法只能继承构造方法,最终返回的fBound匿名函数继承了fToBind对象的构造方法和原型方法,且上下文为oThis,从而实现了绑定。
实际上 anotherFunc =someFunc.bind(oThis, arg1, arg2);中的anotherFunc即为fBound, anotherFunc(args)即执行fBound(args)。