Dziedziczenie JavaScript: Object.create vs new


123

W JavaScript jaka jest różnica między tymi dwoma przykładami:

Warunek wstępny:

function SomeBaseClass(){
}

SomeBaseClass.prototype = {
    doThis : function(){
    },

    doThat : function(){
    }
}

Przykład dziedziczenia A przy użyciu Object.create:

function MyClass(){
}

MyClass.prototype = Object.create(SomeBaseClass.prototype);

Przykład dziedziczenia B przy użyciu nowego słowa kluczowego

function MyClass(){
}

MyClass.prototype = new SomeBaseClass();

Oba przykłady wydają się robić to samo. Kiedy wybrałbyś jedną z nich?

Dodatkowe pytanie: Rozważ kod w poniższym linku (wiersz 15), gdzie w prototypie znajduje się odniesienie do własnego konstruktora funkcji. Dlaczego jest to przydatne?

https://github.com/mrdoob/three.js/blob/master/src/loaders/ImageLoader.js

Fragment (jeśli nie chcesz otwierać linku):

THREE.ImageLoader.prototype = {

    constructor: THREE.ImageLoader
}

23
Dlaczego do cholery jest to oznaczone jako duplikat!?! Drugie pytanie i odpowiedzi nawet nie wspominają Object.create. To pomyłka i należy ją ponownie otworzyć.
Scott Rippey


1
13 głosów na ten komentarz i nadal nie otwarto ponownie ..?!
TJ

Odpowiedzi:


111

W swoim pytaniu wspomniałeś, że Both examples seem to do the same thingto wcale nie jest prawda, ponieważ

Twój pierwszy przykład

function SomeBaseClass(){...}
SomeBaseClass.prototype = {
    doThis : function(){...},
    doThat : function(){...}
}
function MyClass(){...}
MyClass.prototype = Object.create(SomeBaseClass.prototype);

W tym przykładzie po prostu dziedziczysz, SomeBaseClass' prototypeale co, jeśli masz SomeBaseClasspodobną właściwość

function SomeBaseClass(){ 
    this.publicProperty='SomeValue'; 
}

a jeśli używasz go jak

var obj=new MyClass();
console.log(obj.publicProperty); // undefined
console.log(obj);​

objObiekt nie będzie miał publicPropertywłaściwości jak w tym przykładzie .

Twój drugi przykład

MyClass.prototype = new SomeBaseClass();

Wykonuje constructorfunkcję, tworzy instancję SomeBaseClassi dziedziczy cały SomeBaseClassobiekt. Więc jeśli używasz

    var obj=new MyClass();
    console.log(obj.publicProperty); // SomeValue
    console.log(obj);​

W tym przypadku jego publicPropertywłaściwość jest również dostępna dla objobiektu, jak w tym przykładzie .

Ponieważ Object.createnie jest dostępny w niektórych starszych przeglądarkach, w takim przypadku możesz użyć

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

Powyższy kod po prostu dodaje Object.createfunkcję, jeśli nie jest dostępna, więc możesz użyć Object.createfunkcji i myślę, że powyższy kod opisuje, co Object.createfaktycznie robi. Mam nadzieję, że to w jakiś sposób pomoże.


6
Cześć Szejk. Dziękuję za twój wysiłek. Tak, różnica polega na tym, że konstruktor jest uruchamiany w drugim przykładzie, ale nie w pierwszym. (co jest pożądane w moim przypadku). Odnośnie niezdefiniowanej właściwości publicznej, która nie jest dziedziczona z super implementacji, wystarczy wywołać super w konstruktorze dziecka: SomeBaseClass.call (this). Sprawdź to skrzypce: jsfiddle.net/NhQGB
ChrisRich

Szukałem super prostego sposobu na prawidłowe dziedziczenie w JS bez korzystania z bibliotek / frameworków. Myślę, że ten przykład (w moich skrzypcach powyżej) jest najlepszym podejściem dla nowoczesnych przeglądarek. Być może wypełnienie Object.Create mogłoby dodać obsługę starszych przeglądarek?
ChrisRich,

więc w zasadzie podsumowując, jeśli zrobisz .prototype = new, to dziedziczysz wszelkie wartości, które przypisałeś do tego w klasie bazowej, a kiedy robisz object.create, dziedziczysz TYLKO to, co jest w prototypie w klasie bazowej, prawda?
davidjnelson

Czy to „MyClass.prototype = new SomeBaseClass ()” oznacza, że ​​nowa instancja SomeBaseClass jest tworzona, gdy instancja MyClass nie została jeszcze utworzona?

Nie, tylko podczas tworzenia głównego obiektu prototyp jest ustawiony na taki SomeBaseClass.
Alpha,

39

Oba przykłady wydają się robić to samo.

To prawda w twoim przypadku.

Kiedy wybrałbyś jedną z nich?

Gdy SomeBaseClassma treść funkcji, zostanie wykonane za pomocą newsłowa kluczowego. Zwykle nie jest to zamierzone - chcesz tylko skonfigurować łańcuch prototypów. W niektórych przypadkach może to nawet spowodować poważne problemy, ponieważ w rzeczywistości tworzysz instancję obiektu, którego zmienne o zasięgu prywatnym są wspólne dla wszystkich MyClassinstancji, ponieważ dziedziczą one te same uprzywilejowane metody. Można sobie wyobrazić inne skutki uboczne.

Więc generalnie powinieneś preferować Object.create. Jednak nie jest obsługiwany w niektórych starszych przeglądarkach; z tego powodu widzisz to newpodejście zbyt często, ponieważ często nie powoduje ono (oczywistej) szkody. Spójrz także na tę odpowiedź .


To najlepsza odpowiedź, dobrze wyjaśniona
spex

8

Różnica staje się oczywista, jeśli używasz Object.create()go zgodnie z przeznaczeniem. Właściwie to całkowicie ukrywa prototypesłowo przed twoim kodem, wykona pracę pod maską. Używając Object.create(), możemy iść jak

var base =  {
    doThis : function(){
    },

    doThat : function(){
    }
};

A potem możemy z tego rozszerzać / dziedziczyć inne obiekty

var myObject = Object.create( base );
// myObject will now link to "base" via the prototype chain internally

Jest to więc inna koncepcja, bardziej „obiektowy” sposób dziedziczenia. Na przykład nie ma „funkcji konstruktora” po wyjęciu z pudełka Object.create(). Ale oczywiście możesz po prostu utworzyć i wywołać samodzielnie zdefiniowaną funkcję konstruktora w tych obiektach.

Jednym z argumentów za korzystanie Object.create()jest to, że może to wyglądać bardziej naturalny do mix / * * dziedziczą od innych przedmiotów, niż przy użyciu JavaScripts domyślny sposób .


7
Object.createtak naprawdę nie można zastąpić klasycznego podejścia, newkiedy potrzebna jest funkcja konstruktora
Bergi

1
Z całą pewnością może @Bergi. Spójrz na ten wpis na blogu: davidwalsh.name/javascript-objects-deconstruction . Możesz również przekazać obiekt inicjalizacji jako drugi parametr do Object.create ().
LocalPCGuy

@LocalPCGuy: Nie, nie może, ponieważ Object.createnie wywołuje funkcji, która byłaby konieczna do tworzenia domknięć. Oczywiście Object.createmożesz umieścić initmetodę na swoich obiektach i wywołać ją natychmiast, a może nawet zwróci this, aby uzyskać zwięzłą składnię - ale poczekaj, to jest konstruktor.
Bergi

1
Wywołanie funkcji init działa podobnie do konstruktora, ale nie jest konstruktorem. Ta metoda pozwala dokładnie zastąpić klasyczne podejście Object.create. A mentalny model powiązania obiektów jest znacznie prostszy niż model utworzony za pomocą „nowego”.
LocalPCGuy

Cóż, mój mentalny model newużywa „powiązania obiektów”, więc nie ma dużej różnicy. W rzeczywistości są tylko dwie rzeczy: .constructorjest wywoływana .init, a ta funkcja nie ma .prototypewłaściwości wskazującej z powrotem na twój prototypowy obiekt. Reszta to tylko składnia - i wolę new Xod niej Object.create(x).init().
Bergi

0

Nie jestem ekspertem w zakresie skryptów Java, ale oto prosty przykład, aby zrozumieć różnicę między „Object.create” a „new”.

krok 1: utwórz funkcję nadrzędną z pewnymi właściwościami i akcjami.

function Person() {

this.name = 'venkat';

this.address = 'dallas';

this.mobile='xxxxxxxxxx'

}

Person.prototype.func1 = function () {

    return this.name + this.address;
}

krok 2: utwórz funkcję potomną (PersonSalary), która wykracza poza funkcję Person za pomocą słowa kluczowego New .

function PersonSalary() {
    Person.call(this);
}
PersonSalary.prototype = new Person();

PersonSalary();

krok 3: utwórz drugą funkcję potomną (PersonLeaves), która rozszerza funkcję osoby powyżej za pomocą słowa kluczowego Object.create .

function PersonLeaves() {
 Person.call(this);
}
PersonLeaves.prototype = Object.create(Person.prototype);


PersonLeaves();

// Teraz sprawdź prototypy obu funkcji potomnych.

PersonSalary.prototype
PersonLeaves.prototype

obie te funkcje potomne będą łączyły się z prototypem Person (funkcja nadrzędna) i będą miały dostęp do jego metod, ale jeśli utworzysz funkcję potomną za pomocą nowego, zwróci ona zupełnie nowy obiekt ze wszystkimi właściwościami nadrzędnymi, których nie potrzebujemy, a także po utworzeniu jakichkolwiek obiekt lub funkcja za pomocą "New", ta funkcja nadrzędna jest wykonywana, czego nie chcemy.

Oto wnioski na wynos

jeśli chcesz delegować tylko niektóre metody w funkcji nadrzędnej i nie chcesz, aby nowy obiekt był tworzony, najlepszym sposobem jest użycie Object.create.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.