博客原文地址:http://blog.csdn.net/blueblueskyhua/article/details/73135938
前幾天有碰到原型的問題。之前以爲自己對原型還是有所瞭解,但是細細研究,發現自己對原型的理解還是太年輕了。
Object.create 和new
創建對象的方式,我以我碰到的兩種創建方式,Object.create 和new來說明
var Base = function () {}
var o1 = new Base();
var o2 = Object.create(Base);
- 1
- 2
- 3
那這樣到底有什麼不一樣呢?
我先來一段Object.create的實現方式
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
- 1
- 2
- 3
- 4
- 5
可以看出來。Object.create是內部定義一個對象,並且讓F.prototype對象 賦值爲引進的對象/函數 o,並return出一個新的對象。
再看看var o1 = new Base()的時候new做了什麼。
JavaScript 實際上執行的是:
var o1 = new Object();
o1.[[Prototype]] = Base.prototype;
Base.call(o1);
- 1
- 2
- 3
- 4
new做法是新建一個obj對象o1,並且讓o1的__proto__
指向了Base.prototype對象。並且使用call 進行強轉作用環境。從而實現了實例的創建。
我們來看看兩個對象打印情況。
看似是一樣的。
我們對原來的代碼進行改進一下。
var Base = function () {
this.a = 2
}
var o1 = new Base();
var o2 = Object.create(Base);
console.log(o1.a);
console.log(o2.a);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
可以看到Object.create 失去了原來對象的屬性的訪問。
那再看看prototype呢?(一開始沒理解prototype和__proto__
的關係。造成對這兩種方式的創建理解非常費解)。
再一次對代碼進行改進。
var Base = function () {
this.a = 2
}
Base.prototype.a = 3;
var o1 = new Base();
var o2 = Object.create(Base);
console.log(o1.a);
console.log(o2.a);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
我一開始以爲輸出的值是2,3。。。以爲prototype還是存在的。。結果發現真的發錯特錯。我們看運行的結果。
依舊是如此。
那我們就以圖說話。
(F在創建後被銷燬)
看完上圖,我們就知道了,爲什麼通過Object.create構造的連Base原型上的屬性都訪問不到,因爲他壓根就沒有指向他的prototype。這也就說明了__proto__
和 prototype
的區別。所以上面在prototype定義的a,只是Base的prototype對象上的一個屬性。
再來看看就是:
- new關鍵字必須是以function定義的。
- Object.create 則 function和object都可以進行構建。
instanceof 和 isPrototypeOf
寫了創建一個對象實例,並且說了通過原型鏈來完成這一個個對象之間的聯繫,但是你怎麼知道就一定含有呢?所以我們需要一個判斷機制。
function Foo(){
//...
}
Foo.prototype.ff = 2;
var a = new Foo();
a instanceof Foo; //true
- 1
- 2
- 3
- 4
- 5
- 6
instanceof 說的是在a的整條[[Prototype]] 是否含有Foo.prototype對象。 但是這個方法只能實現對象(a)和函數(帶.prototype引用的Foo),如果你想判斷兩個對象(a 和 b)是否通過[[Prototype]]鏈關聯。只用instanceof就無法實現。
所以這裏用到了isPrototypeOf。
var a = {};
var b = Object.ceate(a);
- 1
- 2
b.isPrototypeOf(a);//在a的[[Prototype]]是否出現過b來判斷。
來看看isPrototypeOf實現方式。
function isRelatedTo(o1,o2){
function F(){}
F.prototype = o2;
return o1 instanceof F;
}
- 1
- 2
- 3
- 4
- 5
上述函數通過了構建一個輔助函數F,構建了一個prototype對象。從而達到instanceof比較的條件。
console.log(a.isPrototypeOf(b) === isRelatedTo(b,a));// true
constructor
我們先來看看下面的代碼。
function Foo(){
}
console.log(Foo.prototype.constructor === Foo);//true
var a = new Foo();
console.log(a.constructor === Foo);//true
- 1
- 2
- 3
- 4
- 5
看起來a.constructor === Foo 爲真意味着a的確有一個.constructor指向Foo的.constructor屬性,但事實並不是這樣的。
function Foo(){
}
Foo.prototype = {}
var a1 = new Foo();
console.log(a1.constructor === Foo);//false
console.log(a1.constructor === Object);//true
- 1
- 2
- 3
- 4
- 5
- 6
可以看到a1並沒有.constructor屬性。那是爲什麼呢。?因爲a1沒有.constructor屬性,他會委託[[prototype]]鏈上的Foo.prototype。但是新建的Foo.prototype也沒有.constructor,所以繼續往上找,一直到了頂端的Object.prototype。
你可以手動地進行修正.constructor的指向。
所以可以看出.constructor是一個非常不可靠,並且不安全的引用。在開發中儘量避免使用這些引用,。