函數bind的實現

最近在看《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也同樣,但願大神能解解惑!!!

相關文章
相關標籤/搜索