js中創建對象的方式一般有兩種Object.create和new
const Base = function(){};
const o1 = Object.create(Base);
const o2 = new Base();
在講述兩者區別之前,我們需要知道:
- 構造函數Foo的原型屬性Foo.prototype指向了原型對象。
- 原型對象保存著實例共享的方法,有一個指針constructor指回構造函數。
- js中只有函數有prototype屬性,所有的對象只_proto_隱式屬性。
好了,首先,我們來看看var o1 = new Base()的時候new做了什么。
var o1 = new Object();
o1._proto_ = Base.prototype;
Base.call(o1);
下面這張圖能清晰地看到每個函數和對象之間的關系,用圓形表示對象,方形表示函數。new做的操作就是先創建一個新的對象o1,這時o1._proto_指向Object.prototype。然后更改o1._proto_指向Base.prototype。最后用call強行轉換作用環境,將構造函數的this指向o1,也就是o1擁有了構造函數Base定義的全部屬性。
new過程示意圖.png
再看看Object.create的實現方式
Object.create = function (Base) {
var F = function () {};
F.prototype = Base;
return new F();
};
如下圖所示,首先創建一個空函數F,函數的prototype指向Base函數。new一個函數的實例,即讓該實例的_proto_指向函數F的prototype,也就是Base函數,最后將該實例返回。即通過Object.create創建的對象o2,實際上完成了o2._proto_ = Base的操作。注意傳入的參數Base2是一個對象。如果傳入的是一個構造函數的話,該實例是無法繼承的。
object.create過程示意圖.png
理解他們的內部操作之后,來看個實際例子:
demo1.png
new之后,o1有了Base的全部屬性,所以o1.a 的值是2。由于Object.create傳入的參數是構造函數,o2的_proto_是無法指向Base的,而o2本身是由一個空構造函數F實例化出來的,也不具有a屬性,所以輸出undefined。
假如我們把構造函數Base換成對象
image.png
可以看到,此時o2去原型鏈上找a屬性,o2的_proto_指向的是Base2,所以訪問到屬性a的值為1。