手寫bind前咱們先回顧一下bind有哪些特性,以便更好的理解bind和實現bind。bash
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
複製代碼
能夠看出:app
bind是函數的方法,只有函數能夠調用函數
bind的第一個參數是this指向,剩下的參數做爲調用者的參數post
bind方法返回的是一個函數,須要再次調用才能執行ui
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是怎麼實現的小夥伴必定知道爲何(不知道的能夠看一下這篇文章的末尾 juejin.im/post/5c4926… ) 此時的this指向了res2。 知道了bind的特性,下面咱們來實現一下bindthis
咱們知道bind返回的是一個函數,調用者也是一個函數,而且bind改變了this指向,並且bind還能夠傳參,下面咱們來實現一下這些功能:spa
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,可是原函數的原型新函數並無繼承,因此咱們須要再次繼承一下原型:prototype
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;
}
複製代碼
本覺得大功告成,可是還有一個問題,看下面的例子:code
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;
}
複製代碼
對於代碼有疑問的小夥伴能夠留言或者看註釋!!!