bind函数polyfill源码解析
準備知識
使用new來調用函數會自動執行下面的操作:
注意this綁定規則,new操作具有最高的優先級
《你不知道的JavaScript(上卷)》提供了一個例子,bar被硬綁定到obj上,但是new bar(3) 并沒有像我們預計的那樣把obj.a修改為3。相反,new修改了硬綁定調用bar()中的this。因為使用了new綁定,我們得到了一個名字為baz的新對象,并且baz.a的值為3。
function foo(something) {this.a = something } var obj = {} var bar = foo.bind(obj) bar(2) console.log(obj.a) //2 var baz = new bar(3) console.log(obj.a) //2 console.log(baz.a) //3instanceof運算符的第一個變量是一個對象,暫時稱為A;第二個變量一般是一個函數,暫時稱為B。
instanceof判斷準則:沿著A的__proto__這條線來找,同時沿著B的prototype這條線來找,如果兩條線能找到同一個引用,即同一個對象,那么就返回true。
源碼分析
MDN上提供的polyfill如下,主要的疑惑點應該就是 this instanceof fNOP 作用是什么?
if (!Function.prototype.bind) {Function.prototype.bind = function(oThis) {if (typeof this !== 'function') {throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}var aArgs = Array.prototype.slice.call(arguments, 1),fToBind = this,fNOP = function() {},fBound = function() {return fToBind.apply(this instanceof fNOP // 這段代碼會判斷硬綁定函數是否是被new調用,如果是的話就會使用新創建的this替換硬綁定的this? this: oThis,aArgs.concat(Array.prototype.slice.call(arguments)))}// 維護原型關系if (this.prototype) {// Function.prototype doesn't have a prototype propertyfNOP.prototype = this.prototype; }fBound.prototype = new fNOP()return fBound} }this instanceof fNOP 單獨看是看不明白的,需要結合以下代碼才能說明它的作用
if (this.prototype) {fNOP.prototype = this.prototype; } fBound.prototype = new fNOP()首先我們要清楚:bind(...)會返回一個硬編碼的新函數,它會把參數設置為this的上下文并調用原始函數。
重點就是bind(...)返回的是一個函數!函數!函數!這意味著可以對bind(...)返回的函數進行new操作,那么問題就來了。
this綁定中new操作具有最高的優先級,如果執行new操作,bind(...)應該不起作用,this應該指向new出來的新對象。
清楚了以上內容,我們開始閱讀代碼。
if (typeof this !== 'function') {throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')}bind(...)必須由函數調用,所以以上代碼對this的類型進行檢查,如果不是函數類型則拋出錯誤。
var aArgs = Array.prototype.slice.call(arguments, 1) var fToBind = this var fBound = function() {return fToBind.apply(this instanceof fNOP // 這段代碼會判斷硬綁定函數是否是被new調用,如果是的話就會使用新創建的this替換硬綁定的this? this: oThis,aArgs.concat(Array.prototype.slice.call(arguments)))}aArgs獲取傳入的其它參數,fToBind獲取需要硬綁定的函數,fBound為返回的綁定操作函數,我們先忽略fBound里面的內容,繼續往下看。
if (this.prototype) {fNOP.prototype = this.prototype; } fBound.prototype = new fNOP()我們假設對bind(...)返回的函數進行new操作(原型鏈如下),則this instanceof fNOP 為true,此時我們就知道執行了new操作,硬綁定的this不能生效,需要把this綁定到新生成的對象上。
如果沒有進行new操作的話,就用apply模擬bind綁定,一切按照原計劃進行。
最后我們分析一下維護原型關系的重要性,例子如下:
function Foo(){console.log(this.a);this.a=1; } Foo.prototype.show=function() {console.log(this.a)}; Foo(); // undefined var obj1=new Foo(); obj1.show();var bar=Foo.bind({a:2}); bar(); // 2 var obj2=new bar(); obj2.show();因為bind函數內部保持了原型關系的繼承,所以對象obj2才能訪問到原型上的show方法。
** 注意:Foo.show()是錯誤的,因為Foo的原型指向的是Function.prototype,只有Foo的實例才能調用show方法。
更多專業前端知識,請上 【猿2048】www.mk2048.com
總結
以上是生活随笔為你收集整理的bind函数polyfill源码解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈一下我了解的PWA
- 下一篇: 面试常见问题之实现bind函数