2010-11-12 55 views
308

我最近偶然發現了JavaScript中的Object.create()方法,並試圖推導出它與創建一個對象的新實例的方式不同new SomeFunction(),以及當您會想要使用其中一個。瞭解Object.create()和新SomeFunction()之間的差異

考慮下面的例子:

var test = { 
 
    val: 1, 
 
    func: function() { 
 
    return this.val; 
 
    } 
 
}; 
 
var testA = Object.create(test); 
 

 
testA.val = 2; 
 
console.log(test.func()); // 1 
 
console.log(testA.func()); // 2 
 

 
console.log('other test'); 
 
var otherTest = function() { 
 
    this.val = 1; 
 
    this.func = function() { 
 
    return this.val; 
 
    }; 
 
}; 
 

 
var otherTestA = new otherTest(); 
 
var otherTestB = new otherTest(); 
 
otherTestB.val = 2; 
 
console.log(otherTestA.val); // 1 
 
console.log(otherTestB.val); // 2 
 

 
console.log(otherTestA.func()); // 1 
 
console.log(otherTestB.func()); // 2

注意,相同的行爲在這兩種情況下觀察到的。在我看來,這兩個方案之間的主要區別是:

  • Object.create()使用的對象實際上形成新對象的原型,而在new Function()從聲明的特性/功能不形成雛形。
  • 您不能像使用函數語法一樣使用Object.create()語法創建閉包。鑑於JavaScript的詞法(vs block)類型,這是合乎邏輯的。

上述說明是否正確?我錯過了什麼?你什麼時候使用一個?

編輯:鏈接的jsfiddle版本上面的代碼示例的:http://jsfiddle.net/rZfYL/

+2

請參閱[使用「Object.create」而不是「new」](http:// stackoverflow。com/q/2709612/1048572) – Bergi 2015-07-24 15:04:23

回答

205

Object.create中使用的對象實際上形成新對象的原型,其中在新的Function()窗體中聲明的屬性/函數不構成原型。

是,Object.create構建一個對象,該對象直接從作爲其第一個參數傳遞的對象繼承。

隨着構造函數,新創建的對象從構造的原型繼承,例如:

var o = new SomeConstructor(); 

在上述例子中,從o直接SomeConstructor.prototype繼承。

有一個區別就在這裏,與你Object.create可以創建一個對象,不從任何東西,Object.create(null);繼承,而另一方面,如果設置SomeConstructor.prototype = null;新創建的對象將從Object.prototype繼承。

您不能像使用函數語法一樣使用Object.create語法創建閉包。鑑於JavaScript的詞法(vs block)類型,這是合乎邏輯的。

那麼,你可以創建閉包,例如,使用屬性描述符參數:

var o = Object.create({inherited: 1}, { 
    foo: { 
    get: (function() { // a closure 
     var closured = 'foo'; 
     return function() { 
     return closured+'bar'; 
     }; 
    })() 
    } 
}); 

o.foo; // "foobar" 

請注意,我說的是ECMAScript的第5版Object.create方法,而不是克羅克福德的墊片。

該方法開始在最新的瀏覽器上本地執行,請檢查此compatibility table

+1

@CMS 2個問題。 1)Object.create(null)上的作用域鏈是否仍然在全局作用域(比如瀏覽器中的'window')終止,還是終止於它本身? 2)我還不清楚爲什麼引入了Object.create(例如,什麼功能被遺漏了,這就解決了這個問題?)以及爲什麼會用它來代替新的Function(); – Matt 2010-11-12 16:30:12

+8

@Matt,1)範圍鏈在這裏並不是一個真正的相關概念,範圍鏈與**標識符解析**有關,例如:在當前的*詞法環境中如何解析'foo;'。 2)爲了提供一個簡單的方法來實現繼承,這是一個非常強大的構造。 IMO我會使用它,因爲它非常簡單和輕量級,但對於生產代碼,我們仍然需要等待一段時間,直到ES5得到廣泛支持。關於缺少的功能,創建「原始」對象「Object.create(null);」的事實丟失了,實現可靠的類似哈希表的對象是非常有用的... – CMS 2010-11-12 16:38:32

+0

@CMS謝謝! – Matt 2010-11-12 17:27:37

7

內部Object.create做到這一點:

Object.create = function (o) { 
    function F() {} 
    F.prototype = o; 
    return new F(); 
}; 

語法只是帶走了錯覺,JavaScript使用經典的繼承。

+24

ECMAScript 5 ['Object.create'](http://sideshowbarker.github.com/es5-spec/#x15.2.3.5)方法的功能遠不止於此,您可以通過*屬性描述符定義屬性*,並且可以創建一個不會從任何東西繼承的對象(Object.create(null);'),應該避免使用這種類型的墊片,因爲您無法真正模擬該行爲在ES3上。 [更多信息](http://stackoverflow.com/questions/3830800/object-defineproperty-in-es5/3844768#3844768) – CMS 2010-11-12 16:24:00

+0

嗯,不知道。 – xj9 2010-11-12 19:25:03

+0

同意@CMS,但一般來說,它是Object.create的簡單填充。 – 2017-07-27 21:02:09

166

這裏有兩個電話內部所發生的步驟:
(提示:唯一的區別是在步驟3)


new Test()

  1. 創建new Object() OBJ
  2. obj.__proto__Test.prototype
  3. return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value

Object.create(Test.prototype)

  1. 創建new Object() OBJ
  2. 設置obj.__proto__Test.prototype
  3. return obj;

因此基本上Object.create不執行構造函數。

+4

非常有幫助的比較。 – 2015-03-09 22:30:36

+0

@射線如此使用object.create我們字體有構造函數中提到的函數的屬性? – 2017-02-21 12:28:10

+0

@sortednoun只要屬性是私人的,並沒有在原型上指定,**是的,他們不會被繼承,你不會讓他們在新的對象**(和,我會補充,你可以期望以便在父構造函數至少執行一次的情況下從父類獲得最終的原型屬性)。 – Kamafeather 2017-07-25 16:52:45

16

區別在於所謂的「僞古典與原型遺傳」。建議只在代碼中使用一種類型,而不是混合兩種。

在僞古典繼承(帶有「新」運算符)中,假設您首先定義一個僞類,然後從該類創建對象。例如,定義一個僞類「Person」,然後從「Person」創建「Alice」和「Bob」。

在原型繼承(使用Object.create)中,您直接創建一個特定的人「Alice」,然後使用「Alice」作爲原型創建另一個人「Bob」。這裏沒有「班級」。都是對象。

在內部,JavaScript使用「原型繼承」; 「僞古典」方式只是一些糖。

請參閱this link來比較兩種方式。

+0

這個答案也很好。 +1 – Bento 2016-02-02 17:42:58

297

非常簡單地說,new XObject.create(X.prototype)與另外運行constructor函數。 (並給予constructor機會return實際的對象應該是表達式的結果而不是this。)

就是這樣。 :)

其餘的答案只是令人困惑,因爲顯然沒有人讀取new的定義。)

+18

+1簡單明瞭! (儘管Object.create(null)似乎是一個不錯的選擇 - 可能應該提到這一點)。 – user949300 2014-09-03 04:15:46

+1

最佳答案我見過。非常有幫助! – m0meni 2016-01-26 15:02:14

+0

保持簡單就是要走的路 – Bruce 2016-05-26 12:12:04

20
function Test(){ 
    this.prop1 = 'prop1'; 
    this.prop2 = 'prop2'; 
    this.func1 = function(){ 
     return this.prop1 + this.prop2; 
    } 
}; 

Test.prototype.protoProp1 = 'protoProp1'; 
Test.prototype.protoProp2 = 'protoProp2'; 
var newKeywordTest = new Test(); 
var objectCreateTest = Object.create(Test.prototype); 

/* Object.create */ 
console.log(objectCreateTest.prop1); // undefined 
console.log(objectCreateTest.protoProp1); // protoProp1 
console.log(objectCreateTest.__proto__.protoProp1); // protoProp1 

/* new */ 
console.log(newKeywordTest.prop1); // prop1 
console.log(newKeywordTest.__proto__.protoProp1); // protoProp1 

總結:

1)new關鍵字有兩點需要注意;

一)函數是用來作爲構造

B)function.prototype對象傳遞給__proto__財產......或者__proto__不被支持,這是第二個地方,新的對象看起來找到性能

2)Object.create(obj.prototype)你正在構造一個對象(obj.prototype),並將它傳遞給其計劃的目的..with,現在新對象的__proto__也指向obj.prototype的差異(請xj9爲REF ANS)

31

此:

var foo = new Foo(); 

var foo = Object.create(Foo.prototype); 

十分相似。一個重要的區別是,new Foo實際運行構造函數代碼,而Object.create將不執行代碼,如

function Foo() { 
    alert("This constructor does not run with Object.create"); 
} 

注意,如果您使用的Object.create()雙參數版本,那麼你可以做更多的強大的東西。

+1

很好的解釋。我可以添加,像這樣以最簡單的形式使用Object.create,可以在利用原型繼承的同時,從代碼中省略構造函數。 – 2015-11-19 04:38:38

51

讓我試着解釋(更多Blog):

  1. 當你寫Car構造var Car = function(){},這是怎麼回事兒內部: A diagram of prototypal chains when creating javascript objects 我們一個{prototype}隱藏鏈接Function.prototype這是不可訪問和一個prototype鏈接到Car.prototype這是訪問,並有一個實際constructorCar。 Function.prototype和Car.prototype都隱藏了Object.prototype的鏈接。
  2. 當我們想通過使用new運算符和create方法創建兩個等價對象時,我們必須這樣做:Honda = new Car();Maruti = Object.create(Car.prototype)A diagram of prototypal chains for differing object creation methods 發生了什麼事?

    Honda = new Car(); —當您創建這樣便隱藏{prototype}屬性的對象指向Car.prototype。所以在這裏,本田物件的{prototype}將永遠是Car.prototype —我們沒有任何選擇來更改物件的{prototype}屬性。如果我想改變我們新創建的對象的原型呢?
    Maruti = Object.create(Car.prototype) —當你創建一個這樣的對象時,你有一個額外的選項來選擇你的對象的{prototype}屬性。如果您想將Car.prototype作爲{prototype},則將其作爲函數中的參數傳遞。如果你不想要任何{prototype}對象,那麼你可以通過null像這樣:Maruti = Object.create(null)

結論—利用該方法Object.create你可以自由選擇你的對象{prototype}財產。在new Car();中,你沒有那種自由。在OO的JavaScript

首選方式:

假設我們有兩個對象ab

var a = new Object(); 
var b = new Object(); 

現在,假設a有一些方法,其b也希望訪問。爲此,我們需要對象繼承(a只有在我們需要訪問這些方法時才應該成爲b的原型)。如果我們檢查ab的原型,那麼我們將發現他們共享原型Object.prototype

Object.prototype.isPrototypeOf(b); //true 
a.isPrototypeOf(b); //false (the problem comes into the picture here). 

問題—我們要反對ab原型,但在這裏我們創建的對象b與原型Object.prototype解決方案— ECMAScript 5引入了Object.create(),輕鬆實現了這種繼承。如果我們創建對象b這樣的:

var b = Object.create(a); 

然後,

a.isPrototypeOf(b);// true (problem solved, you included object a in the prototype chain of object b.) 

所以,如果你正在做的面向對象的腳本,然後Object.create()是繼承是非常有用的。

+0

好。我認爲這是觸及牛眼的答案 – Bento 2016-02-02 17:14:23

+0

因此,它與沒有構造函數調用的對象創建有些類似? 我們將享受該課程的所有好處。 obj instanceof類也是如此。但我們不是通過new調用Class函數。 – Praveen 2016-03-30 08:49:09

+0

@Anshul你說'a.isPrototypeOf(b);'會返回false,這是正確的,因爲兩個對象都不同並且指向不同的內存。使用'new'操作符的正確方法就在這裏。 - https://jsfiddle.net/167ununp/。 – 2016-11-10 11:29:19

3

因此對this answerthis videonew關鍵字做接下來的事情就:

  1. 創建新的對象。

  2. 將新對象鏈接到構造函數(prototype)。

  3. 使this變量指向新對象。

  4. 使用新對象執行構造函數並隱式執行return this;

  5. 將構造函數名稱分配給新對象的屬性constructor

Object.create只執行1st2nd步驟!

相關問題