你不知道的javascript之 Object.create 和 new 區別

博客原文地址: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對象上的一個屬性。

再來看看就是:

  1. new關鍵字必須是以function定義的。
  2. 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是一個非常不可靠,並且不安全的引用。在開發中儘量避免使用這些引用,。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章