文章原地址:https://github.com/catchonme/...javascript
ES5
實現 bind
函數以下java
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; }
測試1git
var bar = function() { console.log(this.x) } var foo = { x: 3 } var func = bar.bind(foo); func(); // 3
bar
函數綁定foo
中的x
值,而後輸出3github
bind
函數中最主要的是bound
函數,bound
函數作了哪些事呢?segmentfault
首先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
對象中。app
因此此時直接執行self.call(context)
,返回執行的結果3
,就是咱們測試1中的結果。函數
那何時this instanceof bound == true
呢,並且此時還須要使用空函數F
來獲取主函數的prototype
,post
答案是實例化,何時實例化呢?測試
測試2this
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
賦給新函數,執行新函數,並返回新函數