ES5
實現 bind
函式如下
Function.prototype.bind = function(that){
var self = this,
args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
F = function(){};
var bound = function(){
var context = that, length = arguments.length;
if (this instanceof bound){
F.prototype = self.prototype;
context = new F;
}
var result = (!args && !length)
? self.call(context)
: self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
return context == that ? result : context;
};
return bound;
}
複製程式碼
測試1
var bar = function() {
console.log(this.x)
}
var foo = {
x: 3
}
var func = bar.bind(foo);
func(); // 3
複製程式碼
bar
函式繫結foo
中的x
值,然後輸出3
bind
函式中最主要的是bound
函式,bound
函式做了哪些事呢?
首先context
儲存傳入的that
到context
中,判斷this instanceof bound
,那什麼時候this instanceof bound == true
呢?在測試1中的案例中,this instanceof bound
為false
,列印此時的this
輸出Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
,發現是window
物件,因為foo
本身就是在window
物件中。
所以此時直接執行self.call(context)
,返回執行的結果3
,就是我們測試1中的結果。
那什麼時候this instanceof bound == true
呢,而且此時還需要使用空函式F
來獲取主函式的prototype
,
答案是例項化,什麼時候例項化呢?
測試2
var bar = function() {
console.log(this.x)
}
bar.prototype.name = function(){
this.name = `name`;
}
var foo = {
x: 3
}
var func = bar.bind(foo);
var newFunc = new func; // undefined
newFunc.name(); // name
複製程式碼
對bar.bind(foo)
進行例項化,此時因為進行了new
操作,new
操作做了什麼呢,參考new操作符裡面到底發生了什麼?所以此時的this
為新生成的bound {}
物件,constructor
為bound
函式,所以此時this instanceof bound == true
那為什麼bar.bind(foo)
把foo
物件傳遞的時候,沒有輸出3
而是undefined
呢?也是因為new
操作,當前的上下文已經是新生成的newFunc
函式了。而且當this instanceof bound == true
時,會把bar
的prototype
賦給F
函式,而bound
函式返回的是new F
,所以這時bar
的prototype
也賦給newFunc
了。
我們看看ES6
的操作,結果和上述例子是一樣的。
var bar = function() {
console.log(this.x)
}
bar.prototype.name = function(){
console.log(`name`)
}
var foo = {
x: 3
}
var func = bar.bind(foo);
func(); // 3
// 例項化
var newFunc = new func; // undefined
newFunc.name(); // name
複製程式碼
總結:
所以bind
函式總共做了哪幾件事呢?
- 沒有例項化時,將傳入物件的引數引用到當前函式,執行當前函式,返回結果
- 例項化時,使用
new
操作生成新函式,原函式的prototype
賦給新函式,執行新函式,並返回新函式