Object.create () ve yeni SomeFunction () arasındaki farkı anlama


392

Son zamanlarda Object.create()JavaScript yöntemine tökezledi ve bir nesnenin yeni bir örneğini oluşturmaktan ne zaman farklı olduğunu new SomeFunction()ve ne zaman diğerini kullanmak istediğinizi bulmaya çalışıyorum .

Aşağıdaki örneği düşünün:

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

Her iki durumda da aynı davranışın gözlendiğine dikkat edin. Bana öyle geliyor ki bu iki senaryo arasındaki temel farklar:

  • Kullanılan nesne Object.create()aslında yeni nesnenin prototipini oluştururken new Function(), bildirilen özelliklerden / işlevlerden prototipi oluşturmaz.
  • Object.create()Sözdizimi ile işlevsel sözdiziminde olduğu gibi kapatma oluşturamazsınız . JavaScript'in sözcüksel (blok vs) türü kapsamı düşünüldüğünde bu mantıklıdır.

Yukarıdaki ifadeler doğru mu? Ve bir şey mi kaçırıyorum? Birini diğeri ne zaman kullanırsınız?

EDIT: yukarıdaki kod örneği jsfiddle sürümüne bağlantı: http://jsfiddle.net/rZfYL/


Yanıtlar:


247

Object.create içinde kullanılan nesne aslında yeni nesnenin prototipini oluşturur, burada yeni Function () formunda bildirilen özellikler / işlevler prototipi oluşturmaz.

Evet, Object.createdoğrudan ilk argüman olarak iletilen nesneden miras alan bir nesne oluşturur.

Yapıcı işlevleriyle, yeni oluşturulan nesne yapıcı prototipinden miras alır, örneğin:

var o = new SomeConstructor();

Yukarıdaki örnekte odoğrudan kaynağını devralır SomeConstructor.prototype.

Burada bir fark var, Object.createbir şeyden miras almayan bir nesne yaratabilirsiniz Object.create(null);, diğer yandan, SomeConstructor.prototype = null;yeni oluşturulan nesneyi devralacak şekilde ayarlarsanız Object.prototype.

Object.create sözdizimi ile işlevsel sözdiziminde yaptığınız gibi kapatma oluşturamazsınız. JavaScript'in sözcüksel (blok vs) türü kapsamı düşünüldüğünde mantıklıdır.

Eh, örneğin özellik tanımlayıcıları bağımsız değişkenini kullanarak kapaklar oluşturabilirsiniz:

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

o.foo; // "foobar"

ECMAScript 5. Baskı Object.createyönteminden bahsettiğimi , Crockford'un şiminden değil.

Yöntem, en son tarayıcılarda yerel olarak uygulanmaya başlıyor, bu uyumluluk tablosunu kontrol edin .


2
@ CMS 2 soru. 1) Object.create (null) üzerindeki kapsam zinciri hala global kapsamda (tarayıcıda 'pencere' gibi) sonlanıyor mu yoksa kendi kendine mi sonlanıyor? 2) Bana hala Object.create'ın neden tanıtıldığı (örneğin, bunun ele alındığı hangi özellik eksik?) Ve neden yeni Function () yerine neden kullanılacağı açık değil.
Matt

9
@Matt, 1) kapsam zinciri burada gerçekten ilgili bir kavram değildir, kapsam zinciri tanımlayıcı çözünürlüğü ile ilgilidir , örneğin: foo;mevcut sözlük ortamında nasıl çözülür . 2) Kalıtım uygulamak için kolay bir yol sağlamak, gerçekten güçlü bir yapıdır. IMO kullanacağım çünkü gerçekten basit ve hafif, ancak üretim kodu için ES5 yaygın olarak desteklenene kadar biraz beklememiz gerekiyor. Eksik özellikler hakkında, "bozulmamış" bir nesne oluşturma gerçeği Object.create(null);eksikti, güvenilir karma tablo benzeri nesneler uygulamak gerçekten yararlı ...
CMS

@ CMS Teşekkürler. Yani sadece 'Object.create' kullanarak bir nesne oluşturduğunuzda, prototipi olması gereken nesneyi seçme olanağına sahip olursunuz.
Anshul

@CMS TAMAM, yani yineleme yaparken bok Object.create(null)kullanmak zorunda değilsiniz anlamına gelir hasOwnProperty()çünkü hiçbiri miras ??? Bunu beğendim - teşekkürler. Tabii ki, herkes halahasOwnProperty kullanamayacağı için yapacak, bu Object.create(null)yüzden bunun gerçek bir fayda olduğundan emin değilim ... Şimdiye kadar Object.create()tamamen ikna edici olmayan diğer "faydaları" buldum .
user949300

425

Çok basit bir new Xşekilde Object.create(X.prototype), ek olarak constructorfonksiyonu çalıştırmak olduğunu söyledi . (Ve ifadenin sonucu olması gereken asıl nesneye constructorşans vermek .)returnthis

Bu kadar. :)

Cevapların geri kalanı sadece kafa karıştırıcı, çünkü görünüşe göre hiç kimse newikisinin de tanımını okumuyor . ;)


23
+1 Sadelik ve netlik! (Rağmen Object.create (null) güzel bir seçenek gibi görünüyor - belki de belirtmek gerekir).
user949300

basit ol, işte bu yol
Bill

Bu sadece "bu yüzden, bekleme sorununu bırakır fonksiyonları prototipleri var çok ? O arasındaki ilişki nedir nesne prototipleri?"
Qwertie

3
@Qwertie: JS'de her şey bir nesnedir. :) Bunu SmallTalk'tan kopyalayan Java'dan, sonuna kadar giden Java'dan kopyaladılar. Genel olarak hayatı kolaylaştıran güzel bir “ortaya çıkma” vakası.
Evi1M4chine

@ Evi1M4chine aslında Java'da, işlevler nesne değildir (ve bu konu için de ilkel değildir) ... ve nesnelerin prototipleri yoktur, bu yüzden karşılaştırma uyuşmaz gibi görünür. JS'nin diğer popüler OO dillerinden farklı çalıştığı gerçeği büyük bir karışıklık kaynağıdır (ve tarayıcıların işlevler ve prototipler dahil olmak üzere nesne ağını görselleştirmek için kolay bir yol sağlamamasına yardımcı olmaz). PS Bu bağlantıyı yararlı buldum: davidwalsh.name/javascript-objects-deconstruction
Qwertie

204

Her iki çağrı için dahili olarak gerçekleşen adımlar şunlardır:
(İpucu: tek fark 3. adımdadır)


new Test():

  1. new Object()obj oluştur
  2. set obj.__proto__içinTest.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 oluştur
  2. set obj.__proto__içinTest.prototype
  3. return obj;

Yani temelde Object.createkurucuyu yürütmez.


@ Bu yüzden object.create kullanarak yazı tipini yapıcı fonksiyonunda belirtilen fonksiyonun özelliklerine sahibiz?

@sortednoun, özellikler özel olduğu ve prototipte belirtilmediği sürece, evet, devralınmayacaklar ve bunları yeni nesneye sahip olmayacaksınız (ve ekleyeceğim, nihai prototipli özellikler almayı bekleyebilirsiniz) sadece üst yapıcı en az bir kez yürütüldüğünde).
Kamafeather

Çoğu yapıcı işlevinde olduğu gibi, yöntemler döndürülen nesne içinde tanımlanır, newtemelde tüm işlevler çoğaltılır, ancak Object.createyoktur.
SparK

61

Açıklamaya çalışayım (daha fazla Blog ):

  1. Eğer yazarken Caryapıcı var Car = function(){}, bu işler içten nasıl geçerli: Javascript nesneleri oluştururken prototip zincirlerinin şeması Biz bir var {prototype}gizli bağlantı için Function.prototypehangi erişilebilir değildir ve bir prototypebağlantı Car.prototypehangi erişilebilir ve gerçek sahiptir constructorait Car. Hem Function.prototype hem de Car.prototype'in gizli bağlantıları vardır Object.prototype.
  2. newİşleç ve createyöntemi kullanarak iki eşdeğer nesne oluşturmak istediğimizde, bunu şu şekilde yapmalıyız: Honda = new Car();ve Maruti = Object.create(Car.prototype). Farklı nesne oluşturma yöntemleri için prototip zincirlerinin şeması Ne oluyor?

    Honda = new Car();- Böyle bir nesne oluşturduğunuzda gizli {prototype}özelliğe işaret edilir Car.prototype. Yani burada, {prototype}Honda nesnesinin her zaman olacak Car.prototype- {prototype}nesnenin özelliğini değiştirme seçeneğimiz yok . Yeni oluşturulan nesnemizin prototipini değiştirmek istersem ne olur?
    Maruti = Object.create(Car.prototype)- Böyle bir nesne oluşturduğunuzda, nesnenizin {prototype}özelliğini seçmek için fazladan bir seçeneğiniz olur . Car.prototype olarak istiyorsanız, işlevde {prototype}bir parametre olarak iletin . Eğer herhangi bir istemiyorsanız {prototype}sizin nesne için o zaman iletebilirsiniz nullböyle: Maruti = Object.create(null).

Sonuç - Yöntemi kullanarak Object.createnesne {prototype}özelliğinizi seçme özgürlüğüne sahipsiniz . İçinde new Car();, o özgürlüğe sahip değilsin.

OO JavaScript'te tercih edilen yol:

İki nesneyi olduğunu varsayalım ave b.

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

Şimdi diyelim aki berişmek de isteyen bazı yöntemler var . Bunun için nesne mirasına ihtiyacımız var ( sadece bu yöntemlere erişmek istiyorsak aprototipi olmalı b). Biz prototipleri kontrol ederse ave bo zaman onlar prototipini paylaşan olduğunu göreceksiniz Object.prototype.

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

Sorun - nesnenin aprototipi olarak istiyoruz b, ancak burada bprototiple nesne oluşturduk Object.prototype. Çözüm -Object.create() Bu mirasa kolayca ulaşmak için ECMAScript 5 tanıtıldı . Eğer böyle bir nesne byaratırsak:

var b = Object.create(a);

sonra,

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

Yani, nesne yönelimli komut dosyası yapıyorsanız Object.create(), kalıtım için çok yararlıdır.


Yani, yapıcı çağrısı olmadan nesne yaratmaya biraz benzer mi? Sınıfın tüm avantajlarından yararlanacağız. Class nesnesinin örneği de doğru olacaktır. Ancak Sınıf işlevini yeniyle çağırmıyoruz.
Praveen

@Anshul Bunun doğru a.isPrototypeOf(b);olacağını söylediniz false, çünkü her iki Nesne de farklı ve farklı belleğe işaret ediyor. Bunu newoperatörle yapmanın doğru yolu burada. - jsfiddle.net/167onunp .
Sagar Karira

Bunu yapmak yerine neden b'nin prototip özelliğini a olarak ayarlamıyorsunuz?
Amnestic

Blogunuzdaki makaleyi de beğendim. Kavramı daha iyi anlamama yardımcı oldu. Teşekkür ederim.
steady_daddy

1
Sonuç her şeyi söylüyor.
kushalvm

44

Bu:

var foo = new Foo();

ve

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

oldukça benzer. Önemli bir fark, new Fooaslında yapıcı kodunu Object.createçalıştırırken,

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

İki parametreli sürümünü kullanırsanız, Object.create()çok daha güçlü şeyler yapabileceğinizi unutmayın.


1
Harika bir açıklama. Bunu ekleyebilir miyim, Object.createbunun gibi en basit biçimini kullanarak , prototip mirasından faydalanırken yapıcı işlevlerini kodunuzdan çıkarmanızı sağlar.
Ricky Boyce

23

Fark, sözde "psödoklasik ve prototip palet mirası" dır. Öneri, kodunuzda ikisini karıştırmak yerine yalnızca bir tür kullanmaktır.

Sözde mirasta ("yeni" işleci ile), önce bir sözde sınıf tanımladığınızı ve sonra o sınıftan nesneler oluşturduğunuzu düşünün. Örneğin, sözde sınıf "Kişi" tanımlayın ve sonra "Kişi" den "Alice" ve "Bob" oluşturun.

Prototip palet mirasında (Object.create kullanarak), doğrudan belirli bir kişiyi "Alice" oluşturur ve sonra başka bir kişiyi "Bob" 'u prototip olarak yaratırsınız. Burada "sınıf" yoktur; hepsi nesnelerdir.

Dahili olarak JavaScript "prototypal mirası" kullanır; "psödoklasik" yol sadece biraz şekerdir.

İki yolun karşılaştırması için bu bağlantıya bakın .


21
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

Özet:

1) newanahtar kelime ile dikkat edilmesi gereken iki şey vardır;

a) fonksiyon yapıcı olarak kullanılır

b) function.prototypenesne __proto__özelliğe iletilir ... veya __proto__desteklenmeyen yerlerde, yeni nesnenin özellikleri bulduğu ikinci yerdir

2) Object.create(obj.prototype)sizinle bir nesne ( obj.prototype) oluşturuyor ve onu istenen nesneye __proto__geçiriyorsunuz ... şimdi yeni nesnelerin de obj.prototype'i işaret ettiği fark ile (lütfen bunun için xj9 tarafından ref ans)


15

Nesne oluşturma değişkenleri.


Varyant 1 : ' new Object () ' -> Bağımsız değişken içermeyen nesne oluşturucu.

var p1 = new Object(); // 'new Object()' create and return empty object -> {}

var p2 = new Object(); // 'new Object()' create and return empty object -> {}

console.log(p1); // empty object -> {}

console.log(p2); // empty object -> {}

// p1 and p2 are pointers to different objects
console.log(p1 === p2); // false

console.log(p1.prototype); // undefined

// empty object which is in fact Object.prototype
console.log(p1.__proto__); // {}

// empty object to which p1.__proto__ points
console.log(Object.prototype); // {}

console.log(p1.__proto__ === Object.prototype); // true

// null, which is in fact Object.prototype.__proto__
console.log(p1.__proto__.__proto__); // null

console.log(Object.prototype.__proto__); // null

resim açıklamasını buraya girin


Değişke 2 : ' yeni Nesne (kişi) ' -> Bağımsız değişkenli nesne oluşturucu.

const person = {
    name: 'no name',
    lastName: 'no lastName',
    age: -1
}

// 'new Object(person)' return 'person', which is pointer to the object ->
//  -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p1 = new Object(person);

// 'new Object(person)' return 'person', which is pointer to the object ->
//  -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p2 = new Object(person);

// person, p1 and p2 are pointers to the same object
console.log(p1 === p2); // true
console.log(p1 === person); // true
console.log(p2 === person); // true

p1.name = 'John'; // change 'name' by 'p1'
p2.lastName = 'Doe'; // change 'lastName' by 'p2'
person.age = 25; // change 'age' by 'person'

// when print 'p1', 'p2' and 'person', it's the same result,
// because the object they points is the same
console.log(p1); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(p2); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(person); // { name: 'John', lastName: 'Doe', age: 25 }

resim açıklamasını buraya girin


Varyant 3.1 : ' Object.create (kişi) '. Object.create öğesini basit bir 'person' nesnesiyle kullanın. 'Object.create (person)' yeni boş nesne oluşturur (ve döndürür) ve aynı yeni boş nesneye '__proto__' özelliğini ekler. Bu '__proto__' özelliği 'person' nesnesini gösterecektir.

const person = {
        name: 'no name',
        lastName: 'no lastName',
        age: -1,
        getInfo: function getName() {
           return `${this.name} ${this.lastName}, ${this.age}!`;
    }
}

var p1 = Object.create(person);

var p2 = Object.create(person);

// 'p1.__proto__' and 'p2.__proto__' points to
// the same object -> 'person'
// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(p1.__proto__);
console.log(p2.__proto__);
console.log(p1.__proto__ === p2.__proto__); // true

console.log(person.__proto__); // {}(which is the Object.prototype)

// 'person', 'p1' and 'p2' are different
console.log(p1 === person); // false
console.log(p1 === p2); // false
console.log(p2 === person); // false

// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);

console.log(p1); // empty object - {}

console.log(p2); // empty object - {}

// add properties to object 'p1'
// (properties with the same names like in object 'person')
p1.name = 'John';
p1.lastName = 'Doe';
p1.age = 25;

// add properties to object 'p2'
// (properties with the same names like in object 'person')
p2.name = 'Tom';
p2.lastName = 'Harrison';
p2.age = 38;

// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);

// { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);

// { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);

// use by '__proto__'(link from 'p1' to 'person'),
// person's function 'getInfo'
console.log(p1.getInfo()); // John Doe, 25!

// use by '__proto__'(link from 'p2' to 'person'),
// person's function 'getInfo'
console.log(p2.getInfo()); // Tom Harrison, 38!

resim açıklamasını buraya girin


Varyant 3.2 : ' Object.create (Object.prototype) '. Object.create öğesini yerleşik nesne ile kullanın -> 'Object.prototype'. 'Object.create (Object.prototype)' yeni boş nesne oluşturur (ve döndürür) ve aynı yeni boş nesneye '__proto__' özelliğini ekler. Bu '__proto__' özelliği 'Object.prototype' nesnesini işaret edecektir.

// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p1' property '__proto__', which is link to 'Object.prototype'
var p1 = Object.create(Object.prototype);

// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p2' property '__proto__', which is link to 'Object.prototype'
var p2 = Object.create(Object.prototype);

console.log(p1); // {}

console.log(p2); // {}

console.log(p1 === p2); // false

console.log(p1.prototype); // undefined

console.log(p2.prototype); // undefined

console.log(p1.__proto__ === Object.prototype); // true

console.log(p2.__proto__ === Object.prototype); // true

resim açıklamasını buraya girin


Değişke 4 : ' yeni SomeFunction () '

// 'this' in constructor-function 'Person'
// represents a new instace,
// that will be created by 'new Person(...)'
// and returned implicitly
function Person(name, lastName, age) {

    this.name = name;
    this.lastName = lastName;
    this.age = age;

    //-----------------------------------------------------------------
    // !--- only for demonstration ---
    // if add function 'getInfo' into
    // constructor-function 'Person',
    // then all instances will have a copy of the function 'getInfo'!
    //
    // this.getInfo: function getInfo() {
    //  return this.name + " " + this.lastName + ", " + this.age + "!";
    // }
    //-----------------------------------------------------------------
}

// 'Person.prototype' is an empty object
// (before add function 'getInfo')
console.log(Person.prototype); // Person {}

// With 'getInfo' added to 'Person.prototype',
// instances by their properties '__proto__',
// will have access to the function 'getInfo'.
// With this approach, instances not need
// a copy of the function 'getInfo' for every instance.
Person.prototype.getInfo = function getInfo() {
    return this.name + " " + this.lastName + ", " + this.age + "!";
}

// after function 'getInfo' is added to 'Person.prototype'
console.log(Person.prototype); // Person { getInfo: [Function: getInfo] }

// create instance 'p1'
var p1 = new Person('John', 'Doe', 25);

// create instance 'p2'
var p2 = new Person('Tom', 'Harrison', 38);

// Person { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);

// Person { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);

// 'p1.__proto__' points to 'Person.prototype'
console.log(p1.__proto__); // Person { getInfo: [Function: getInfo] }

// 'p2.__proto__' points to 'Person.prototype'
console.log(p2.__proto__); // Person { getInfo: [Function: getInfo] }

console.log(p1.__proto__ === p2.__proto__); // true

// 'p1' and 'p2' points to different objects(instaces of 'Person')
console.log(p1 === p2); // false

// 'p1' by its property '__proto__' reaches 'Person.prototype.getInfo' 
// and use 'getInfo' with 'p1'-instance's data
console.log(p1.getInfo()); // John Doe, 25!

// 'p2' by its property '__proto__' reaches 'Person.prototype.getInfo' 
// and use 'getInfo' with 'p2'-instance's data
console.log(p2.getInfo()); // Tom Harrison, 38!

resim açıklamasını buraya girin


Güzel Özet. Teşekkürler. Bugün bana yardımcı oldu !!
Anandaraja_Srinivasan

11

Dahili olarak Object.createbunu yapar:

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

Sözdizimi, JavaScript'in Klasik Kalıtım kullandığı yanılsamasını ortadan kaldırır.


25
ECMAScript 5 Object.createyöntemi bundan çok daha fazlasını yapar, özellikleri özellik tanımlayıcılarına göre tanımlayabilir ve herhangi bir şeyden miras almayan bir nesne oluşturabilirsiniz ( Object.create(null);), bu tür dolgulardan kaçınılmalıdır çünkü ES3'teki davranış. Daha fazla bilgi
CMS

@CMS ile aynı fikirde olmakla birlikte, genel olarak, basit çoklu dolgudur Object.create.
V. Kovpak

10

Buna göre hiç bu cevap ve bu videonun new anahtar kelimenin sonraki şey yapar:

  1. Yeni nesne oluşturur.

  2. Yeni nesneyi yapıcı işlevine ( prototype) bağlar .

  3. Yapar thisyeni nesneye değişken nokta.

  4. Yeni nesneyi ve örtülü gerçekleştirme özelliğini kullanarak yapıcı işlevini yürütür return this;

  5. Oluşturucu işlev adını yeni nesnenin özelliğine atar constructor.

Object.createsadece gerçekleştirir 1stve 2ndadımlar !!!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.