從規範來看,Function.prototype.bind
是如何工做,以及如何來模擬bind操做。react
以下簡單示例,普通對象 testObj
內部有一個b函數,接受一個普通參數,若參數爲空則輸出 this.a
。git
const testObj = { a: 3, b: function(args) { console.log(args || this.a); }, }; testObj.b() testObj.b(23) const c = testObj.b c() c(23) const c1 = testObj.b.bind(testObj, 50) c1(70)
查看結果:github
testObj.b
被從新賦值給 c
後,函數的的執行上下文已經改變,致使輸出爲 undefined
。經過上面例子,若是採用 bind
後,則能夠改變 testObj
的執行上下文,並能夠把默認值傳遞到參數函數列表.app
假若在 testObj.b
內,加入 console.log(arguments)
, 則能夠看到以下輸出:函數
50 70
bind函數是,Function
原型鏈上的函數,主要是改變函數執行上下文的同時,能夠傳入函數參數值,並返回新的函數this
如圖是mdn上的定義,es5
bind產生的函數就一個偏函數,就是說使用bind能夠參數一個函數,而後接受新的參數。spa
In computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.
詳細可看: https://github.com/mqyqingfeng/Blog/issues/43
在EcmaScript的規範 15.3.4.5 中以下截圖:prototype
bind 方法須要一個或更多參數,thisArg 和(可選的)arg1, arg2, 等等,執行以下步驟返回一個新函數對象: 1. 令 Target 爲 this 值 . 2. 若是 IsCallable(Target) 是 false, 拋出一個 TypeError 異常 . 3. 令 A 爲一個(可能爲空的)新內部列表,它包含按順序的 thisArg 後面的全部參數(arg1, arg2 等等)。 4. 令 F 爲一個新原生 ECMAScript 對象。 5. 依照 8.12 指定,設定 F 的除了 [[Get]] 以外的全部內部方法。 6. 依照 15.3.5.4 指定,設定 F 的 [[Get]] 內部屬性。 7. 設定 F 的 [[TargetFunction]] 內部屬性爲 Target。 8. 設定 F 的 [[BoundThis]] 內部屬性爲 thisArg 的值。 9. 設定 F 的 [[BoundArgs]] 內部屬性爲 A。 10. 設定 F 的 [[Class]] 內部屬性爲 "Function"。 11. 設定 F 的 [[Prototype]] 內部屬性爲 15.3.3.1 指定的標準內置 Function 的 prototype 對象。 12. 依照 15.3.4.5.1 描述,設定 F 的 [[Call]] 內置屬性。 13. 依照 15.3.4.5.2 描述,設定 F 的 [[Construct]] 內置屬性。 14. 依照 15.3.4.5.3 描述,設定 F 的 [[HasInstance]] 內置屬性。 15. 若是 Target 的 [[Class]] 內部屬性是 "Function", 則 a. 令 L 爲 Target 的 length 屬性減 A 的長度。 b. 設定 F 的 length 自身屬性爲 0 和 L 中更大的值。 16. 不然設定 F 的 length 自身屬性爲 0. 17. 設定 F 的 length 自身屬性的特性爲 15.3.5.1 指定的值。 18. 設定 F 的 [[Extensible]] 內部屬性爲 true。 19. 令 thrower 爲 [[ThrowTypeError]] 函數對象 (13.2.3)。 20. 以 "caller", 屬性描述符 {[[Get]]: thrower, [[Set]]: thrower,[[Enumerable]]: false, [[Configurable]]: false}, 和 false 做爲參數調用 F 的 [[DefineOwnProperty]] 內部方法。 21. 以 "arguments", 屬性描述符 {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, 和 false 做爲參數調用 F 的 [[DefineOwnProperty]] 內部方法。 22. 返回 F. bind 方法的 length 屬性是 1。 Function.prototype.bind 建立的函數對象不包含 prototype 屬性或 [[Code]], [[FormalParameters]], [[Scope]] 內部屬
規範表達過程簡述以下:code
thisArg
以外的其餘函數thisArg
新的函數的 this.[[Call]]
, [[Construct]]
, [[HasInstance]]
, 讓對象能夠被調用[[Function]]
, 並設置新的函數 length
, 新的length值 範圍是 0 ~ Target.length
caller
屬性,arguments
在bind過程當中,會從新設置
[[Call]]
相關函數內部方法,詳細能夠看規範。
知道了過程,假若不支持,該如何呢?來,搞一個手工的:
Function.prototype.bind2 = function bind2(thisArgs) { const aArgs = Array.prototype.slice.call(arguments, 1); const fThis = this; const fNOP = function() {}; const fBound = function() { // 這段判斷是否是使用bind返回函數繼續bind return fThis.apply(this instanceof fBound ? this : fThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; // this === Function.prototype, 保證建立函數的原型鏈也爲undefined if (this.prototype) fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; };
咱們實現方法依賴一些屬性方法:
並且咱們實現方法有一個問題在於:
prototype
, 不符合規範查看更規範的實現,點擊這裏
歡迎交流