手寫bind前咱們先回顧一下bind有哪些特性,以便更好的理解bind和實現bind。app
var obj = { a: 100, say(one, two) { console.log(this.a, one, two); } } var obj2 = { a: 300 } var res = obj.say.bind(obj2, 1, 2); res(); //300 1 2
能夠看出:函數
bind是函數的方法,只有函數能夠調用post
bind的第一個參數是this指向,剩下的參數做爲調用者的參數this
bind方法返回的是一個函數,須要再次調用才能執行prototype
function test(){ this.a = 10, this.b = 20 }; var foo = { a:200 } var res = test.bind(foo); var res2 = new res(); console.log(res2); //test {a: 10, b: 20}
從上面能夠看出,new以後this執行不跟隨bind的第一個參數了,知道new是怎麼實現的小夥伴必定知道爲何(不知道的能夠看一下這篇文章的末尾 http://www.javashuo.com/article/p-qubdlsoj-kz.html ) 此時的this指向了res2。
知道了bind的特性,下面咱們來實現一下bindcode
咱們知道bind返回的是一個函數,調用者也是一個函數,而且bind改變了this指向,並且bind還能夠傳參,下面咱們來實現一下這些功能:對象
Function.prototype.bind = function(oThis) { // 判斷調用者是否是函數 if(typeof this != 'function'){ throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } //保存this(this指向調用bind者) var fToBind = this; //獲取傳入bind函數的第二個及其後面的參數(除去this參數) var aArgs = Array.prototype.slice.call(arguments,1); var fBound = function(){ //this instanceof fBound === true時,說明返回的fBound被當作new的構造函數調用 //== false的時候說明當作了普通函數來調用,this爲bind的第一個參數 return fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments))); } // 返回新函數 return fBound; }
上面的代碼實現了一個基本的bind,可是還有一些問題,例如上面只是綁定了this,可是原函數的原型新函數並無繼承,因此咱們須要再次繼承一下原型:繼承
Function.prototype.bind = function(oThis) { // 判斷調用者是否是函數 if(typeof this != 'function'){ throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } //保存this(this指向調用bind者) var fToBind = this; //獲取傳入bind函數的第二個及其後面的參數(除去this參數) var aArgs = Array.prototype.slice.call(arguments,1); var fBound = function(){ //this instanceof fBound === true時,說明返回的fBound被當作new的構造函數調用 //== false的時候說明當作了普通函數來調用,this爲bind的第一個參數 return fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments))); } //綁定原型 fBound.prototype = this.prototype; // 返回新函數 return fBound; }
本覺得大功告成,可是還有一個問題,看下面的例子:get
function bar() {} var bindFoo = bar.bind(null); bindFoo.prototype.value = 1; console.log(bar.prototype.value) // 1
我只改變了bindFoo的原型,bar的爲何也跟着變了,由於在寫bind的時候把bar的原型賦給了bindFoo,因此致使了這種狀況,下面咱們用一箇中轉的函數來解決這個問題:原型
Function.prototype.bind = function(oThis) { // 判斷調用者是否是函數 if(typeof this != 'function'){ throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } //保存this(this指向調用bind者) var fToBind = this; //獲取傳入bind函數的第二個及其後面的參數(除去this參數) var aArgs = Array.prototype.slice.call(arguments,1); var fNOP = function() {}; var fBound = function(){ //this instanceof fBound === true時,說明返回的fBound被當作new的構造函數調用 //== false的時候說明當作了普通函數來調用,this爲bind的第一個參數 return fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments))); } // 爲了讓 fBound 構造的實例可以繼承綁定函數的原型中的值 if (this.prototype) { fNOP.prototype = this.prototype; } // 下行的代碼使fBound.prototype是fNOP的實例,所以 // 返回的fBound若做爲new的構造函數,new生成的新對象做爲this傳入fBound,新對象的__proto__就是fNOP的實例 fBound.prototype = new fNOP(); // 返回新函數 return fBound; }
對於代碼有疑問的小夥伴能夠留言或者看註釋!!!