使用new來調用函數會自動執行下面的操做:javascript
注意this綁定規則,new操做具備最高的優先級java
《你不知道的JavaScript(上卷)》提供了一個例子,bar被硬綁定到obj上,可是new bar(3) 並無像咱們預計的那樣把obj.a修改成3。相反,new修改了硬綁定調用bar()中的this。由於使用了new綁定,咱們獲得了一個名字爲baz的新對象,而且baz.a的值爲3。bash
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) //3 複製代碼
instanceof運算符的第一個變量是一個對象,暫時稱爲A;第二個變量通常是一個函數,暫時稱爲B。markdown
instanceof判斷準則:沿着A的__proto__這條線來找,同時沿着B的prototype這條線來找,若是兩條線能找到同一個引用,即同一個對象,那麼就返回true。app
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 property fNOP.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的上下文並調用原始函數。this
重點就是bind(...)返回的是一個函數!函數!函數!這意味着能夠對bind(...)返回的函數進行new操做,那麼問題就來了。編碼
this綁定中new操做具備最高的優先級,若是執行new操做,bind(...)應該不起做用,this應該指向new出來的新對象。spa
清楚了以上內容,咱們開始閱讀代碼。
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方法。