最近在看《MVC的JavaScript Web富應用開發》,其中出現了ES5新增的bind函數的實現代碼,本人菜鳥一枚,花了一天才搞明白,代碼先上:javascript
if (!Function.prototype.bind) {
Function.prototype.bind = function (obj) {
var slice = [].slice,
args = slice.call(arguments, 1),
self = this,
nop = function () {},
bound = function () {
return self.apply( this instanceof nop ? this : (obj || {}),
args.concat(slice.call(arguments)));
};
nop.prototype = self.prototype;
bound.prototype = new nop();
return bound;
};
}
本文假設讀者水平和我差很少,一步一步進行分析,高手勿噴,若是能夠幫我解答一下最後的疑問,thx。java
開始實現這個函數面試
根據bind返回的函數的做用的不一樣,這個函數有兩種狀況:(1)作普通函數(2)作構造函數數組
當新函數是普通函數時,代碼爲:markdown
Function.prototype.bind = function (obj) {
var slice = [].slice,
args = slice.call(arguments, 1),
self = this,
bound = function () {
return self.apply( obj || {}, args.concat(slice.call(arguments)));
};
return bound;
};
代碼中:閉包
[].slice.call(arguments, 1) 或者 Array.prototype.slice.call(argumens, 1)
由於arguments不是真的數組,不能改變,因此經過[].slice.call(arguments)來返回參數數組。app
args.concat(slice.call(arguments))
把兩個參數數組整合到一塊兒函數
另外,代碼中 return bound; 涉及到閉包(或者函數柯里化),這裏不展開,若是閉包的概念不知道能夠搜索一下,面試常常問到。ui
當新函數用做構造函數時,上面的代碼就不適用了,以下:this
var Book = function(name, price){
this.name = name;
this.price = price;
};
var NoteBook = Book.bind(null ,'notebook'),
n = new NoteBook(13);
console.log(n) // 實際:{}, 預期:{name: 'notebook', price: 13}
說明上面的代碼須要完善,在這以前,咱們先了解一下函數用new的實際過程(《JavaScript高程序設計》P145)
1.建立一個新對象
2.將構造函數的做用域賦給了this對象(所以this指向這個新對象)
3.執行構造函數中的代碼
4.若是沒有return,自動返回this,不然返回你返回的對象
如今解釋爲何會返回 {}:
經過上面的步驟,咱們能夠知道關鍵就是self.apply調用時傳入的對象不是實例對象,因此改變它就行了。
因此將obj || {}改爲:this instanceof bound ? this : (obj || {}),這樣就能夠順利返回實例,同時在執行Book函數時能保證this指向該實例,代碼變成:
Function.prototype.bind = function (obj) {
var slice = [].slice,
args = slice.call(arguments, 1),
self = this,
bound = function () {
return self.apply( this instanceof bound ? this : (obj || {}),
args.concat(slice.call(arguments)));
};
return bound;
};
到這裏解決了一些問題,可是還不夠,不要忘記了,咱們在Book的原型上可能定義了一下方法或者屬性。
...... Book.prototype.say = function(){ console.log(this.name); }; ......
以上代碼可沒有辦法作到繼承到Book原型上的方法,因此應該bound應該繼承Book的原型方法
Function.prototype.bind = function (obj) {
var slice = [].slice,
args = slice.call(arguments, 1),
self = this,
nop = function () {},
bound = function () {
return self.apply( this instanceof bound ? this : (obj || {}),
args.concat(slice.call(arguments)));
};
nop.prototype = self.prototype;
bound.prototype = new nop();
return bound;
};
到此和書上代碼就基本同樣了,除了書上用的是:
this instanceof nop ? this : (obj || {})
這個就是個人疑問了,個人感受就是這裏nop換成bound也同樣,但願大神能解解惑!!!