昨天看到一個面試題將的自己實現一個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 的問題
我們來看這個截圖,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指向,那麼誰更優先順序更高一些呢?
複製程式碼
傳入的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的原型, 作者才疏學淺, 如有錯誤請多指教,請多包涵複製程式碼