回顧Javascript建構函式

凹凸卿發表於2019-03-01

長期更新文章 喜歡的start下 github.com/aototo/blog

建構函式已經是老生常談的事情了。這裡講一些比較基礎的東西。

先看下一個例子

function Book(name) { 
    if (!(this instanceof Book)) { 
        // the constructor was called without "new".
        return new Book(name);
    } 
}

var myBook = Book(name);
var myBook1 = Book(name);
myBook.constructor === myBook1.constructor // true複製程式碼

首先判斷this是否為Book的例項,不是就返回新的例項。經常用於解決在建構函式前面忘記使用new的情況,如果沒有使用在function前面使用new,那就按正常的函式執行。那為什麼這裡可以這麼使用?

我們先看下new的原理

(1) 建立一個新的物件: var myBook = New Object()
(2) 設定物件的__proto__ 指向建構函式的prototype
    myBook.__proto__ = Book.prototype複製程式碼

到了第二步驟我們就可以看到myBook可以訪問建構函式的prototype的constructor。

var myBook = New Object();
myBook.__proto__ = Book.prototype;
myBook instanceof Book // true複製程式碼

當執行第二步驟以後例子中的 if (!(this instanceof Book)) 就不會被執行。所以this instanceof Book可以判斷當前函式是否使用new。從而避免開發者忽略而造成程式錯誤的情況。

(3)第三步就是執行Book函式,並且讓this指向myBook物件複製程式碼

第四步就是判斷Book返回值的型別:

判斷條件
(1)如果是值型別就丟棄,返回instance。
(2)如果是引用型別,就返回這個引用型別的物件,替換掉instance。複製程式碼

例子:

function Book() {
    return 2;
}
var myBook = new Book();
myBook instanceof Book // true複製程式碼

return 2屬於值型別,包括String、布林值、null、undefined等..如果是值型別,就丟棄返回instance。

function Book() {
    var newObj = {}
    return newObj;
}
var myBook = new Book();
myBook instanceof Book // false複製程式碼

如果是引用型別,就返回這個引用型別的物件

function Book() {
    return new Number();
}
...複製程式碼

如果return的值是一個Number 物件,那麼例項物件的值就是Number 物件。

使用Object.create()建立的物件其proto 並不是指向建構函式。

原型鏈繼承需要注意的一點

例子:

function Car() {}
function Bmw() {}

Bmw.prototype = new Car();
Bmw.prototype.constructor = Bmw; 

var bmw1 = new Bmw();複製程式碼

比較奇怪的是 Bmw.prototype.constructor = Bmw; 解釋下為什麼要這麼處理。
假設如果沒有這行

bmw1.constructor === Car //true複製程式碼

Bmw.prototype 實際上是 new Car() 的例項,結果導致Bmw prototype中的 constructor從丟失(ps: function建立後prototype已經有constructor值),
bmw1物件在原型鏈中查詢constructor的時候指向了建構函式Car,這明顯是錯誤的。因此這裡修正了Bmw.prototype.constructor = Bmw。同時還可以通過proto獲取Car的建構函式。

bmw1.__proto__.__proto__.constructor === Car複製程式碼

相關文章