Warisan JavaScript: Object.create vs new

123

Dalam JavaScript, apa perbedaan antara kedua contoh ini:

Prasyarat:

function SomeBaseClass(){
}

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

    doThat : function(){
    }
}

Contoh pewarisan A menggunakan Object.create:

function MyClass(){
}

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

Contoh pewarisan B menggunakan kata kunci baru

function MyClass(){
}

MyClass.prototype = new SomeBaseClass();

Kedua contoh tersebut tampaknya melakukan hal yang sama. Kapan Anda akan memilih salah satu dari yang lain?

Pertanyaan tambahan: Pertimbangkan kode di tautan di bawah (baris 15), di mana referensi ke konstruktor fungsi itu sendiri disimpan dalam prototipe. Mengapa ini berguna?

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

Kutipan (jika Anda tidak ingin membuka tautan):

THREE.ImageLoader.prototype = {

    constructor: THREE.ImageLoader
}
ChrisRich
sumber
23
Kenapa ini ditandai sebagai duplikat!?! Pertanyaan dan jawaban lainnya, bahkan tidak disebutkan Object.create. Ini adalah kesalahan, dan harus dibuka kembali.
Scott Rippey
2
Jika ada, itu adalah duplikat dari stackoverflow.com/questions/4166616/…
Scott Rippey
1
13 suara pada komentar itu dan masih belum dibuka kembali ..?!
TJ

Jawaban:

111

Dalam pertanyaan Anda, Anda telah menyebutkan bahwa Both examples seem to do the same thing, Itu tidak benar sama sekali, karena

Contoh pertama Anda

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

Dalam contoh ini, Anda hanya mewarisi SomeBaseClass' prototypetetapi bagaimana jika Anda memiliki properti yang Anda SomeBaseClasssukai

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

dan jika Anda menggunakannya seperti

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

The objobjek tidak akan memiliki publicPropertyproperti seperti dalam contoh ini .

Contoh kedua Anda

MyClass.prototype = new SomeBaseClass();

Ini menjalankan constructorfungsi, membuat instance dari SomeBaseClassdan mewarisi seluruh SomeBaseClassobjek. Jadi, jika Anda menggunakan

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

Dalam hal ini publicPropertypropertinya juga tersedia untuk objobjek seperti dalam contoh ini .

Karena Object.createtidak tersedia di beberapa browser lama, dalam hal ini Anda dapat menggunakan

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

Kode di atas hanya menambahkan Object.createfungsi jika tidak tersedia sehingga Anda dapat menggunakan Object.createfungsi dan saya pikir kode di atas menjelaskan apa yang Object.createsebenarnya dilakukan. Semoga ini bisa membantu.

Alfa
sumber
6
Hai Sheikh. Terima kasih atas usaha Anda dalam hal ini. Ya, perbedaannya adalah konstruktor dijalankan pada contoh kedua tetapi tidak pada contoh pertama. (yang diinginkan dalam kasus saya). Mengenai properti publik yang tidak ditentukan yang tidak diwarisi dari implementasi super, Anda hanya perlu memanggil super di konstruktor anak: SomeBaseClass.call (this). Periksa biola ini: jsfiddle.net/NhQGB
ChrisRich
Saya telah mencari cara super sederhana untuk pewarisan yang tepat di JS tanpa menggunakan perpustakaan / kerangka kerja apa pun. Saya pikir contoh ini (dalam biola saya di atas) adalah pendekatan terbaik untuk browser modern. Mungkin Object.Create polyfill dapat menambahkan dukungan untuk browser lama?
ChrisRich
jadi pada dasarnya untuk meringkas, jika Anda melakukan .prototype = new maka Anda mewarisi nilai apa pun yang Anda tetapkan untuk ini di kelas dasar, dan ketika Anda melakukan object.create Anda HANYA mewarisi apa yang ada di prototipe di kelas dasar, bukan?
davidjnelson
Apakah ini "MyClass.prototype = new SomeBaseClass ()" berarti bahwa instance baru SomeBaseClass dibuat ketika instance MyClass belum dibuat?
Tidak, hanya ketika Anda membuat objek utama, prototipe disetel untuk itu SomeBaseClass.
Alpha
39

Kedua contoh tersebut tampaknya melakukan hal yang sama.

Itu benar dalam kasus Anda.

Kapan Anda akan memilih salah satu dari yang lain?

Ketika SomeBaseClassmemiliki tubuh fungsi, ini akan dieksekusi dengan newkata kunci. Ini biasanya tidak dimaksudkan - Anda hanya ingin menyiapkan rantai prototipe. Dalam beberapa kasus, bahkan dapat menyebabkan masalah serius karena Anda benar-benar membuat instance objek, yang variabel cakupan pribadinya dibagikan oleh semua MyClassinstance karena mereka mewarisi metode istimewa yang sama. Efek samping lain bisa dibayangkan.

Jadi, biasanya Anda lebih suka Object.create. Namun, ini tidak didukung di beberapa browser lama; yang merupakan alasan Anda melihat new-pendekatan terlalu sering karena sering tidak membahayakan (jelas). Lihat juga jawaban ini .

Bergi
sumber
Ini adalah jawaban terbaik, dijelaskan dengan baik
spex
8

Perbedaannya menjadi jelas jika Anda menggunakan Object.create()sebagaimana mestinya. Sebenarnya, itu sepenuhnya menyembunyikan prototypekata dari kode Anda, itu akan melakukan pekerjaan di bawah tenda. Menggunakan Object.create(), kita bisa pergi seperti

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

    doThat : function(){
    }
};

Dan kemudian kita dapat memperluas / mewarisi objek lain dari ini

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

Jadi ini adalah konsep lain, cara pewarisan yang lebih "berorientasi objek". Tidak ada "fungsi konstruktor" di luar kotak menggunakan Object.create()misalnya. Tapi tentu saja Anda bisa membuat dan memanggil fungsi konstruktor yang ditentukan sendiri di dalam objek tersebut.

Salah satu argumen untuk digunakan Object.create()adalah bahwa mungkin terlihat lebih alami untuk mencampur / * mewarisi * dari objek lain, daripada menggunakan cara default Javascripts .

jAndy
sumber
7
Object.createtidak dapat benar-benar menggantikan pendekatan klasik dengan newsaat fungsi konstruktor diperlukan
Bergi
1
Pasti bisa @Bergi. Lihat entri blog ini: davidwalsh.name/javascript-objects-deconstruction . Anda juga bisa meneruskan objek inisialisasi sebagai parameter kedua ke Object.create ().
LocalPCGuy
@LocalPCGuy: Tidak bisa, karena Object.createtidak memanggil fungsi yang akan diperlukan untuk membuat penutupan. Tentu saja, dengan Object.createAnda dapat meletakkan initmetode pada objek Anda dan memanggilnya segera dan bahkan mungkin kembali thissehingga Anda mendapatkan sintaks yang ringkas - tapi tunggu, itu adalah konstruktor.
Bergi
1
Memanggil fungsi init berfungsi mirip dengan konstruktor, tetapi ini bukan konstruktor. Dan metode itu memungkinkan Anda mengganti pendekatan klasik dengan Object.create. Dan model mental dari keterkaitan objek jauh lebih sederhana daripada yang dibuat melalui 'baru'.
LocalPCGuy
Nah, model mental saya newmenggunakan "object linkage", jadi tidak banyak perbedaan. Faktanya, hanya ada dua hal: .constructordipanggil .init, dan fungsi itu tidak memiliki .prototypeproperti yang menunjuk kembali ke objek prototipe Anda. Sisanya hanya sintaks - dan saya lebih memilih new Xlebih Object.create(x).init().
Bergi
0

Saya bukan ahli dalam java script tapi berikut adalah contoh sederhana untuk memahami perbedaan antara "Object.create" dan "new" ..

langkah 1: buat fungsi induk dengan beberapa properti dan tindakan ..

function Person() {

this.name = 'venkat';

this.address = 'dallas';

this.mobile='xxxxxxxxxx'

}

Person.prototype.func1 = function () {

    return this.name + this.address;
}

langkah 2: buat fungsi anak (PersonSalary) yang meluas di atas fungsi Person menggunakan kata kunci baru ..

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

PersonSalary();

langkah 3: buat fungsi anak kedua (PersonLeaves) yang meluas di atas fungsi Person menggunakan kata kunci Object.create ..

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


PersonLeaves();

// Sekarang periksa kedua prototipe fungsi anak.

PersonSalary.prototype
PersonLeaves.prototype

kedua fungsi anak ini akan ditautkan ke prototipe Person (fungsi induk) dan dapat mengakses metodenya tetapi jika Anda membuat fungsi anak menggunakan new itu akan mengembalikan objek baru dengan semua properti induk yang tidak kita perlukan dan juga saat Anda membuatnya objek atau fungsi menggunakan "New" sehingga fungsi induk dijalankan yang tidak kita inginkan.

Berikut adalah kesimpulannya

jika Anda hanya ingin mendelegasikan ke beberapa metode dalam fungsi induk dan tidak ingin objek baru dibuat, menggunakan Object.create adalah cara terbaik.

Venkat
sumber