javascript bind polyfill

Tmier發表於2018-05-25

昨天看到一個面試題將的自己實現一個bind函式,自己思考了一下決定寫寫試試.

1 首先明確第一個功能實現this的顯示繫結

bind函式最明顯的功能就是實現this的顯示繫結, bind返回的是一個新函式:
複製程式碼
Function.prototype.bind = function() {
   var slice = Array.prototype.slice,
       context = arguments[0],
       _this = this,
       outerArg = slice.call(arguments, 1);
       return function() {
           _this.apply(context, outerArg)
       }
}
複製程式碼
這種寫法實現了最基礎的this顯示繫結,然而bind還是一種函式的柯里化,能夠先傳參,下面對程式碼稍加進行更改.
複製程式碼

2 實現柯里化

Function.prototype.bind = function() {
    var slice = Array.prototype.slice,
        context = arguments[0],
        _this = this,
        outerArg = slice.call(arguments, 1);
        return function() {
            var innerArg = slice.call(argunments); // 新增
            var arg = outerArg.contact(innerArg); // 新增
            _this.apply(context, arg)
        }
 }
複製程式碼
這樣就實現了柯里化和this的顯示繫結, 但是這並沒有結束. 大多數情況下我們現在寫的bind都能滿足我們的使用,但是官方文件上有這麼一句話
    A bound function may also be constructed using the new operator: doing
    so acts as though the target function had instead been constructed.
    The provided this value is ignored, while prepended arguments are
    provided to the emulated function.
    
這句話敘述的是關於函式被bind後如果我們對他進行new 操作後的行為;
複製程式碼

3 this 的問題

javascript bind polyfill

我們來看這個截圖,testBind是我們上面所寫的bind的實現方式, 當我們對fn進行testBind繫結後傳入context(這裡是obj), 
調retFn進行 new操作後 他返回的是一個空物件, 再看看 我們用原生的bind去繫結fn傳入obj 對retBindFn進行new操作後會返回一個物件包含a屬性.
這個差異說明了我們目前實現的testBind函式還是有功能欠缺的, 那欠缺的在哪裡呢?

this! 沒錯就是this. 原生的bind雖然把使用bind顯示繫結將函式內部this指向我們傳入的context
但是new操作是將函式內部的this指向他new操作後反返回的例項, 兩個操作都是改變this指向,那麼誰更優先順序更高一些呢?
複製程式碼

javascript bind polyfill

傳入的obj中的a屬性沒變!!!  new操作更厲害,優先順序更高一些.
那說明在這兩種情況同時存在的情況下函式內部的this要指向函式本身的this(因為new操作會將此this指向new操作返回的例項);
那這個操作應該如何實現呢, 這裡可以藉助一箇中間函式;
複製程式碼
   Function.prototype.bind = function() {
       var slice = Array.prototype.slice,
           context = arguments[0],
           _this = this,
           outerArg = slice.call(arguments, 1);
       var retFn = function() {
           var innerArg = slice.call(argunments); 
           var arg = outerArg.contact(innerArg); 
           var oThis = this instanceof F ? this : context
           _this.apply(oThis, arg)
       }
       var F = function() {}
       F.prototype = this.prototype;
       retFn.prototype = new F()
       return retFn
   }
複製程式碼
藉助F函式根據我的理解是讓我們區分這裡是是否使用了new 
F.prototype = this.prototype;
retFn.prototype = new F()
如果沒有這兩句, 我們所有的new retFn(), 內部的this指向的是例項本身即為 Object 的例項。
不使用new 操作 retFn 內部this指向的是context(不傳的話一般採用預設的this繫結,this指向的是window)也是 Object 的例項。

retFn.prototype = new F()
我們沒法判斷是否使用new操作, 而藉助F函式 如果使用new RetFn()的話 首先內部this 就是F的例項也是Object的例項。
這樣的話我們就能能夠通過這種方法判斷是否使用new 操作從而選擇this的指向.
F.prototype = this.prototype; 是在糾正retFn的原型, 作者才疏學淺, 如有錯誤請多指教,請多包涵複製程式碼

相關文章