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.