Mengapa perlu mengatur prototipe konstruktor?


294

Di bagian tentang pewarisan dalam artikel MDN Pengantar Javascript Berorientasi Objek , saya perhatikan mereka mengatur prototype.constructor:

// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;  

Apakah ini melayani tujuan penting? Apakah saya tetap bisa mengabaikannya?


23
Senang Anda bertanya ini: Saya membaca dokumentasi yang sama kemarin dan ingin tahu tentang alasan di balik menetapkan konstruktor secara eksplisit juga.
Wylie

6
Saya hanya harus menunjukkan ini, pertanyaan ini sekarang terhubung di artikel yang Anda tautkan!
Marie

7
tidak ada yang perlu
nothingisnecessary

1
The subclass.prototype.constructorakan menunjuk parent_classjika Anda tidak menulis subclass.prototype.constructor = subclass; Artinya, menggunakan subclass.prototype.constructor()secara langsung akan menghasilkan hasil yang tidak terduga.
KuanYu Chu

Jawaban:


263

Ini tidak selalu diperlukan, tetapi memang ada kegunaannya. Misalkan kita ingin membuat metode salin pada Personkelas dasar . Seperti ini:

// define the Person Class  
function Person(name) {
    this.name = name;
}  

Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new this.constructor(this.name);
};  

// define the Student class  
function Student(name) {  
    Person.call(this, name);
}  

// inherit Person  
Student.prototype = Object.create(Person.prototype);

Sekarang apa yang terjadi ketika kita membuat yang baru Studentdan menyalinnya?

var student1 = new Student("trinth");  
console.log(student1.copy() instanceof Student); // => false

Salinan bukan turunan dari Student. Ini karena (tanpa pemeriksaan eksplisit), kami tidak memiliki cara untuk mengembalikan Studentsalinan dari kelas "basis". Kami hanya dapat mengembalikan a Person. Namun, jika kami telah mengatur ulang konstruktor:

// correct the constructor pointer because it points to Person  
Student.prototype.constructor = Student;

... maka semuanya berfungsi seperti yang diharapkan:

var student1 = new Student("trinth");  
console.log(student1.copy() instanceof Student); // => true

34
Catatan: constructorAtribut tidak memiliki arti khusus dalam JS, jadi Anda bisa menyebutnya bananashake. Satu-satunya perbedaan adalah bahwa mesin otomatis menginisialisasi constructorpada f.prototypesetiap kali Anda menyatakan fungsi f. Namun, itu dapat ditimpa kapan saja.
user123444555621

58
@ Pumbaa80 - Saya mengerti maksud Anda, tetapi kenyataan bahwa mesin secara otomatis menginisialisasi constructorberarti bahwa memang memiliki makna khusus dalam JS, cukup banyak menurut definisi.
Wayne

13
Saya hanya ingin menjelaskan bahwa alasan mengapa perilaku Anda mengatakan karya ini karena Anda menggunakan return new this.constructor(this.name);bukannya return new Person(this.name);. Sejak this.constructoradalah Studentfungsi (karena Anda mengaturnya dengan Student.prototype.constructor = Student;), yang copyfungsi berakhir memanggil Studentfungsi. Saya tidak yakin apa maksud Anda dengan //just as badkomentar tersebut.
CEGRD

12
@ lwburk apa yang Anda maksud dengan "// sama buruknya"?
CEGRD

6
Saya rasa saya mengerti. Tapi, bagaimana jika Studentkonstruktor telah menambahkan sebuah argumen tambahan seperti: Student(name, id)? Apakah kita kemudian harus mengesampingkan copyfungsi, memanggil Personversi dari dalamnya, dan kemudian juga menyalin idproperti tambahan ?
snapfractalpop

76

Apakah ini melayani tujuan penting?

Iya dan tidak.

Di ES5 dan sebelumnya, JavaScript sendiri tidak digunakan constructoruntuk apa pun. Itu didefinisikan bahwa objek default pada properti fungsi prototypeakan memilikinya dan itu akan merujuk kembali ke fungsi, dan hanya itu . Tidak ada hal lain dalam spesifikasi yang dimaksud sama sekali.

Itu berubah dalam ES2015 (ES6), yang mulai menggunakannya dalam kaitannya dengan hierarki warisan. Misalnya, Promise#thenmenggunakan constructorproperti dari janji yang Anda panggil (melalui SpeciesConstructor ) ketika membangun janji baru untuk kembali. Ini juga terlibat dalam array subtyping (via ArraySpeciesCreate ).

Di luar bahasa itu sendiri, kadang-kadang orang akan menggunakannya ketika mencoba membangun fungsi "klon" generik atau hanya secara umum ketika mereka ingin merujuk pada apa yang mereka yakini sebagai fungsi konstruktor objek. Pengalaman saya adalah bahwa menggunakannya jarang, tetapi kadang-kadang orang menggunakannya.

Apakah saya tetap bisa mengabaikannya?

Itu ada di sana secara default, Anda hanya perlu mengembalikannya ketika Anda mengganti objek pada properti fungsi prototype:

Student.prototype = Object.create(Person.prototype);

Jika Anda tidak melakukan ini:

Student.prototype.constructor = Student;

... lalu Student.prototype.constructormewarisi dari Person.prototypeyang (mungkin) miliki constructor = Person. Jadi itu menyesatkan. Dan tentu saja, jika Anda mensubclassing sesuatu yang menggunakannya (suka Promiseatau Array) dan tidak menggunakan class¹ (yang menangani ini untuk Anda), Anda harus memastikan bahwa Anda menyetelnya dengan benar. Jadi pada dasarnya: Ini ide yang bagus.

Tidak apa-apa jika tidak ada dalam kode Anda (atau kode perpustakaan yang Anda gunakan) menggunakannya. Saya selalu memastikan itu terhubung dengan benar.

Tentu saja, dengan classkata kunci ES2015 (alias ES6) , sebagian besar waktu kita akan menggunakannya, kita tidak perlu lagi, karena itu ditangani untuk kita ketika kita melakukannya

class Student extends Person {
}

¹ "... jika kamu mensubclassingkan sesuatu yang menggunakannya (suka Promiseatau tidak Array) dan tidak menggunakan class..."  - Itu mungkin untuk melakukan itu, tapi itu sangat menyakitkan (dan agak konyol). Anda harus menggunakan Reflect.construct.


12

TLDR; Tidak super diperlukan, tetapi mungkin akan membantu dalam jangka panjang, dan lebih akurat untuk melakukannya.

CATATAN: Banyak yang diedit karena jawaban saya sebelumnya ditulis secara membingungkan dan memiliki beberapa kesalahan yang saya lewatkan saat terburu-buru untuk menjawab. Terima kasih kepada mereka yang menunjukkan beberapa kesalahan mengerikan.

Pada dasarnya, ini untuk mengirim subclass dengan benar di Javascript. Ketika kita subkelas, kita harus melakukan beberapa hal funky untuk memastikan bahwa delegasi prototipe berfungsi dengan benar, termasuk menimpa prototypeobjek. Menimpa prototypeobjek termasuk constructor, jadi kita perlu memperbaiki referensi.

Mari kita cepat melihat bagaimana 'kelas' dalam ES5 bekerja.

Katakanlah Anda memiliki fungsi konstruktor dan prototipe:

//Constructor Function
var Person = function(name, age) {
  this.name = name;
  this.age = age;
}

//Prototype Object - shared between all instances of Person
Person.prototype = {
  species: 'human',
}

Ketika Anda memanggil konstruktor untuk instantiate, katakan Adam:

// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);

Kata newkunci yang dipanggil dengan 'Orang' pada dasarnya akan menjalankan konstruktor Orang dengan beberapa baris kode tambahan:

function Person (name, age) {
  // This additional line is automatically added by the keyword 'new'
  // it sets up the relationship between the instance and the prototype object
  // So that the instance will delegate to the Prototype object
  this = Object.create(Person.prototype);

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

  return this;
}

/* So 'adam' will be an object that looks like this:
 * {
 *   name: 'Adam',
 *   age: 19
 * }
 */

Jika kita console.log(adam.species), lookup akan gagal di adamcontoh, dan mencari rantai prototypal untuk nya .prototype, yang Person.prototype- dan Person.prototype memiliki sebuah .speciesproperti, sehingga pencarian akan berhasil di Person.prototype. Ini kemudian akan masuk'human' .

Di sini, surat Person.prototype.constructorwasiat akan menunjuk dengan benar Person.

Jadi sekarang bagian yang menarik, yang disebut 'subclassing'. Jika kita ingin membuat Studentkelas, yaitu subkelas dari Personkelas dengan beberapa perubahan tambahan, kita perlu memastikan bahwa Student.prototype.constructorpoin ke Siswa untuk akurasi.

Itu tidak melakukan ini dengan sendirinya. Saat Anda subkelas, kode ini terlihat seperti ini:

var Student = function(name, age, school) {
 // Calls the 'super' class, as every student is an instance of a Person
 Person.call(this, name, age);
 // This is what makes the Student instances different
 this.school = school
}

var eve = new Student('Eve', 20, 'UCSF');

console.log(Student.prototype); // this will be an empty object: {}

Memanggil di new Student()sini akan mengembalikan objek dengan semua properti yang kita inginkan. Di sini, jika kita periksa eve instanceof Person, itu akan kembali false. Jika kami mencoba mengakses eve.species, itu akan kembaliundefined .

Dengan kata lain, kita perlu mengirimkan delegasi sehingga eve instanceof Personmengembalikan true dan agar instance Studentdelegasi benar Student.prototype, dan kemudian Person.prototype.

TETAPI karena kami menyebutnya dengan newkata kunci, ingat apa yang ditambahkan oleh doa itu? Itu akan memanggil Object.create(Student.prototype), yang bagaimana kita mengatur hubungan delegasi antara Studentdan Student.prototype. Perhatikan bahwa saat ini, Student.prototypekosong. Jadi mencari .speciescontoh Studentakan gagal karena delegasi untuk hanya Student.prototype , dan .speciesproperti tidak ada di Student.prototype.

Ketika kita melakukan assign Student.prototypeuntuk Object.create(Person.prototype), Student.prototypedirinya kemudian delegasi untuk Person.prototype, dan mencari eve.speciesakan kembali humanseperti yang kita harapkan. Mungkin kita ingin mewarisi dari Student.prototype AND Person.prototype. Jadi kita harus memperbaiki semua itu.

/* This sets up the prototypal delegation correctly 
 *so that if a lookup fails on Student.prototype, it would delegate to Person's .prototype
 *This also allows us to add more things to Student.prototype 
 *that Person.prototype may not have
 *So now a failed lookup on an instance of Student 
 *will first look at Student.prototype, 
 *and failing that, go to Person.prototype (and failing /that/, where do we think it'll go?)
*/
Student.prototype = Object.create(Person.prototype);

Sekarang delegasi berfungsi, tetapi kami menimpa Student.prototypedengan dari Person.prototype. Jadi jika kita memanggil Student.prototype.constructor, itu akan menunjuk ke Personbukan Student. Ini sebabnya kami harus memperbaikinya.

// Now we fix what the .constructor property is pointing to    
Student.prototype.constructor = Student

// If we check instanceof here
console.log(eve instanceof Person) // true

Di ES5, constructorproperti kami adalah referensi yang merujuk ke fungsi yang telah kami tulis dengan maksud untuk menjadi 'konstruktor'. Selain dari apanew diberikan kata kunci kepada kami, konstruktor adalah fungsi 'polos'.

Dalam ES6, constructorsekarang dibangun ke cara kita menulis kelas - seperti pada, itu disediakan sebagai metode ketika kita mendeklarasikan kelas. Ini hanyalah gula sintaksis tetapi itu memberi kita beberapa kenyamanan seperti akses ke superketika kita memperluas kelas yang ada. Jadi kita akan menulis kode di atas seperti ini:

class Person {
  // constructor function here
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  // static getter instead of a static property
  static get species() {
    return 'human';
  }
}

class Student extends Person {
   constructor(name, age, school) {
      // calling the superclass constructor
      super(name, age);
      this.school = school;
   }
}

eve instanceof Studentdikembalikan true. Lihat stackoverflow.com/questions/35537995/… untuk penjelasan. Juga ketika Anda mengatakan which is, at the moment, nothingapa yang Anda maksud? Setiap fungsi memiliki prototipe jadi jika saya periksa Student.prototypeitu adalah sesuatu.
Aseem Bansal

Kesalahanku. Seharusnya membaca 'eve instanceof Person' yang akan kembali salah. Saya akan mengubah bagian itu. Anda benar bahwa setiap fungsi memiliki properti prototipe. Namun , tanpa menetapkan prototipe Object.create(Person.prototype), Student.prototypekosong. Jadi jika kita masuk eve.species, ia tidak akan mendelegasikan dengan benar hingga superclass-nya, Person, dan itu tidak akan masuk 'human'. Agaknya, kami ingin setiap subclass mewarisi dari prototipe dan juga prototipe supernya.
bthehuman

Untuk memperjelas, which is, at the moment, nothingmaksud saya, Student.prototypebenda itu kosong.
bthehuman

Lebih lanjut tentang prototipe: Tanpa penugasan Student.prototypeke Object.create(Person.prototype)- yang, jika Anda ingat, cara yang sama semua contoh Person diatur untuk mendelegasikan ke Person.prototype- mencari properti pada contoh Studentakan didelegasikan hanya untuk Student.prototype. Jadi eve.speciesakan gagal pencariannya. Jika kita menugaskannya, Student.prototypeitu sendiri yang kemudian didelegasikan Person.prototype, dan mencari eve.speciesakan kembali human.
bthehuman

Tampaknya ada beberapa hal yang salah di sini: "Ini perlu ketika Anda mencoba untuk meniru 'subklasifikasi' [...] sehingga ketika Anda memeriksa apakah sebuah instance adalah instancekonstruktor 'subkelas', itu akan akurat." Tidak, instanceoftidak digunakan constructor. "Namun, jika kita mencari .prototype.constructor siswa, itu masih akan menunjuk ke Person" Tidak, itu akan menjadi Student. Saya tidak mengerti maksud dari contoh ini. Memanggil fungsi dalam konstruktor bukanlah warisan. "Dalam ES6, konstruktor sekarang merupakan fungsi aktual dan bukan referensi ke fungsi" Uh apa?
Felix Kling

10

Saya tidak setuju. Tidak perlu mengatur prototipe. Ambil kode yang sama persis tetapi hapus garis prototype.constructor. Apakah ada yang berubah? Tidak. Sekarang, buat perubahan berikut:

Person = function () {
    this.favoriteColor = 'black';
}

Student = function () {
    Person.call(this);
    this.favoriteColor = 'blue';
}

dan di akhir kode uji ...

alert(student1.favoriteColor);

Warnanya akan biru.

Perubahan pada prototype.constructor, menurut pengalaman saya, tidak melakukan banyak hal kecuali Anda melakukan hal-hal yang sangat spesifik, sangat rumit yang mungkin bukan praktik yang baik :)

Sunting: Setelah menyodok sekitar web sebentar dan melakukan beberapa eksperimen, sepertinya orang mengatur konstruktor sehingga 'terlihat' seperti hal yang sedang dibangun dengan 'baru'. Saya kira saya berpendapat bahwa masalah dengan ini adalah bahwa javascript adalah bahasa prototipe - tidak ada yang namanya pewarisan. Tetapi kebanyakan programmer berasal dari latar belakang pemrograman yang mendorong pewarisan sebagai 'jalan'. Jadi kami datang dengan segala macam hal untuk mencoba dan membuat bahasa prototipikal ini menjadi bahasa 'klasik' .. seperti memperluas 'kelas'. Sungguh, dalam contoh yang mereka berikan, siswa baru adalah seseorang - itu bukan 'perpanjangan' dari siswa lain .. siswa itu semua tentang orang itu, dan apa pun orang itu, siswa itu juga. Perpanjang siswa, dan apa pun yang Anda

Crockford agak gila dan terlalu bersemangat, tetapi lakukan beberapa bacaan serius pada beberapa hal yang ditulisnya .. itu akan membuat Anda melihat hal ini dengan sangat berbeda.


8
Ini tidak mewarisi rantai prototipe.
Cypher

1
@ Chypher lambat bertepuk tangan menyambut percakapan, empat tahun kemudian. Ya, rantai prototipe yang diwariskan, terlepas dari apakah Anda menimpa prototype.constructor tersebut. Cobalah mengujinya.
Stephen

7
Anda kehilangan kode yang mewarisi prototipe. Selamat datang di internet.
Cypher

1
Cuplikan Kode @Cypher didasarkan pada kode di artikel yang ditautkan. Selamat membaca pertanyaan secara keseluruhan. Oh Tunggu.
Stephen

1
@macher, aku memakainya sebagai warisan klasik. Pilihan kata yang buruk di pihak saya.
Stephen

9

Ini memiliki jebakan besar jika Anda menulis

Student.prototype.constructor = Student;

tetapi kemudian jika ada seorang Guru yang prototipenya juga Orang dan Anda menulis

Teacher.prototype.constructor = Teacher;

maka konstruktor Siswa sekarang adalah Guru!

Sunting: Anda dapat menghindari ini dengan memastikan bahwa Anda telah menetapkan prototipe Siswa dan Guru menggunakan instance baru dari kelas Person yang dibuat menggunakan Object.create, seperti pada contoh Mozilla.

Student.prototype = Object.create(Person.prototype);
Teacher.prototype = Object.create(Person.prototype);

1
Student.prototype = Object.create(...)diasumsikan dalam pertanyaan ini. Jawaban ini tidak menambah apa pun kecuali kemungkinan kebingungan.
André Chalella 6-15

3
@ AndréNeves Saya menemukan jawaban ini bermanfaat. Object.create(...)digunakan dalam artikel MDN yang menelurkan pertanyaan, tetapi tidak dalam pertanyaan itu sendiri. Saya yakin banyak orang tidak mengklik.
Alex Ross

Artikel yang ditautkan direferensikan dalam pertanyaan sebelum menggunakan Object.create (). Jawaban ini dan Sunting dari jawaban itu tidak benar-benar relevan, dan itu membingungkan untuk sedikitnya :-)
Drenai

1
Poin yang lebih luas adalah bahwa ada gotcha yang akan menarik orang-orang baru ke prototipe Javascript. Jika kita membahas di 2016, maka Anda harus benar-benar menggunakan kelas ES6, Babel, dan / atau naskah. Tetapi jika Anda benar-benar ingin membuat kelas secara manual dengan cara ini, ada baiknya untuk memahami bagaimana rantai prototipe benar-benar bekerja untuk meningkatkan kekuatan mereka. Anda dapat menggunakan objek apa pun sebagai prototipe, dan mungkin Anda tidak ingin baru objek terpisah. Selain itu, kembali sebelum HTML 5 tersebar luas, Object.create tidak selalu tersedia, jadi lebih mudah untuk membuat kelas yang salah.
James D

5

Sejauh ini kebingungan masih ada.

Mengikuti contoh asli, karena Anda memiliki objek yang ada student1sebagai:

var student1 = new Student("Janet", "Applied Physics");

Misalkan Anda tidak ingin tahu cara student1dibuat, Anda hanya ingin objek lain seperti itu, Anda dapat menggunakan properti konstruktor student1seperti:

var student2 = new student1.constructor("Mark", "Object-Oriented JavaScript");

Di sini akan gagal mendapatkan properti dari Studentjika properti konstruktor tidak disetel. Sebaliknya itu akan membuat Personobjek.


2

Punya contoh kode yang bagus tentang mengapa benar-benar perlu untuk mengatur konstruktor prototipe ..

function CarFactory(name){ 
   this.name=name;  
} 
CarFactory.prototype.CreateNewCar = function(){ 
    return new this.constructor("New Car "+ this.name); 
} 
CarFactory.prototype.toString=function(){ 
    return 'Car Factory ' + this.name;
} 

AudiFactory.prototype = new CarFactory();      // Here's where the inheritance occurs 
AudiFactory.prototype.constructor=AudiFactory;       // Otherwise instances of Audi would have a constructor of Car 

function AudiFactory(name){ 
    this.name=name;
} 

AudiFactory.prototype.toString=function(){ 
    return 'Audi Factory ' + this.name;
} 

var myAudiFactory = new AudiFactory('');
  alert('Hay your new ' + myAudiFactory + ' is ready.. Start Producing new audi cars !!! ');            

var newCar =  myAudiFactory.CreateNewCar(); // calls a method inherited from CarFactory 
alert(newCar); 

/*
Without resetting prototype constructor back to instance, new cars will not come from New Audi factory, Instead it will come from car factory ( base class )..   Dont we want our new car from Audi factory ???? 
*/

Anda createNewCarmetode menciptakan pabrik !? Juga sepertinya ini seharusnya digunakan var audiFactory = new CarFactory("Audi")daripada menggunakan warisan.
Bergi

Contoh Anda menggunakan this.constructorinternal, jadi tidak mengherankan jika harus ditetapkan. Apakah Anda punya contoh tanpa itu?
Dmitri Zaitsev

1

Tidak perlu untuk fungsi bergula 'kelas' atau menggunakan 'Baru' hari ini. Gunakan objek literal.

Prototipe Obyek sudah menjadi 'kelas'. Ketika Anda mendefinisikan objek literal, itu sudah merupakan contoh dari objek prototipe. Ini juga dapat bertindak sebagai prototipe objek lain, dll.

const Person = {
  name: '[Person.name]',
  greeting: function() {
    console.log( `My name is ${ this.name || '[Name not assigned]' }` );
  }
};
// Person.greeting = function() {...} // or define outside the obj if you must

// Object.create version
const john = Object.create( Person );
john.name = 'John';
console.log( john.name ); // John
john.greeting(); // My name is John 
// Define new greeting method
john.greeting = function() {
    console.log( `Hi, my name is ${ this.name }` )
};
john.greeting(); // Hi, my name is John

// Object.assign version
const jane = Object.assign( Person, { name: 'Jane' } );
console.log( jane.name ); // Jane
// Original greeting
jane.greeting(); // My name is Jane 

// Original Person obj is unaffected
console.log( Person.name ); // [Person.name]
console.log( Person.greeting() ); // My name is [Person.name]

Ini layak dibaca :

Bahasa berorientasi objek berbasis kelas, seperti Java dan C ++, didirikan pada konsep dua entitas yang berbeda: kelas dan instance.

...

Bahasa berbasis prototipe, seperti JavaScript, tidak membuat perbedaan ini: ia hanya memiliki objek. Bahasa berbasis prototipe memiliki gagasan tentang objek prototipe, objek yang digunakan sebagai templat untuk mendapatkan properti awal untuk objek baru. Objek apa pun dapat menentukan propertinya sendiri, baik saat Anda membuatnya atau pada saat dijalankan. Selain itu, objek apa pun dapat dikaitkan sebagai prototipe untuk objek lain, memungkinkan objek kedua berbagi properti objek pertama


1

Hal ini diperlukan ketika Anda membutuhkan alternatif toStringtanpa monkeypatching:

//Local
foo = [];
foo.toUpperCase = String(foo).toUpperCase;
foo.push("a");
foo.toUpperCase();

//Global
foo = [];
window.toUpperCase = function (obj) {return String(obj).toUpperCase();}
foo.push("a");
toUpperCase(foo);

//Prototype
foo = [];
Array.prototype.toUpperCase = String.prototype.toUpperCase;
foo.push("a");
foo.toUpperCase();

//toString alternative via Prototype constructor
foo = [];
Array.prototype.constructor = String.prototype.toUpperCase;
foo.push("a,b");
foo.constructor();

//toString override
var foo = [];
foo.push("a");
var bar = String(foo);
foo.toString = function() { return bar.toUpperCase(); }
foo.toString();

//Object prototype as a function
Math.prototype = function(char){return Math.prototype[char]};
Math.prototype.constructor = function() 
  {
  var i = 0, unicode = {}, zero_padding = "0000", max = 9999;
  
  while (i < max) 
    {
    Math.prototype[String.fromCharCode(parseInt(i, 16))] = ("u" + zero_padding + i).substr(-4);

    i = i + 1;
    }    
  }

Math.prototype.constructor();
console.log(Math.prototype("a") );
console.log(Math.prototype["a"] );
console.log(Math.prototype("a") === Math.prototype["a"]);


Apa yang seharusnya dilakukan dari semua ini? foo.constructor()??
Ry-

0

EDIT, saya sebenarnya salah. Mengomentari batas tidak mengubah perilaku sama sekali. (Saya mengujinya)


Ya itu perlu. Saat kamu melakukan

Student.prototype = new Person();  

Student.prototype.constructormenjadi Person. Oleh karena itu, panggilan Student()akan mengembalikan objek yang dibuat oleh Person. Jika Anda melakukannya

Student.prototype.constructor = Student; 

Student.prototype.constructordiatur ulang ke Student. Sekarang ketika Anda menyebutnya Student()dijalankan Student, yang memanggil konstruktor induk Parent(), ia mengembalikan objek yang diwarisi dengan benar. Jika Anda tidak menyetel ulang Student.prototype.constructorsebelum memanggilnya, Anda akan mendapatkan objek yang tidak memiliki properti yang disetel Student().


3
Konstruksi prototipe dapat menjadi seseorang, tetapi itu tepat karena mewarisi semua properti dan metode dari Orang tersebut. Membuat Siswa baru () tanpa mengatur prototype.constructor secara tepat memanggil konstruktornya sendiri.
Stephen

0

Diberikan fungsi konstruktor sederhana:

function Person(){
    this.name = 'test';
}


console.log(Person.prototype.constructor) // function Person(){...}

Person.prototype = { //constructor in this case is Object
    sayName: function(){
        return this.name;
    }
}

var person = new Person();
console.log(person instanceof Person); //true
console.log(person.sayName()); //test
console.log(Person.prototype.constructor) // function Object(){...}

Secara default (dari spesifikasi https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor ), semua prototipe secara otomatis mendapatkan properti bernama konstruktor yang menunjuk kembali ke fungsi. yang merupakan properti. Bergantung pada konstruktornya, properti dan metode lain mungkin ditambahkan ke prototipe yang bukan praktik yang sangat umum tetapi masih diizinkan untuk ekstensi.

Jadi cukup menjawab: kita perlu memastikan bahwa nilai dalam prototype.constructor diatur dengan benar seperti yang seharusnya oleh spesifikasi.

Apakah kita harus selalu menetapkan nilai ini dengan benar? Ini membantu dengan debugging dan membuat struktur internal konsisten terhadap spesifikasi. Kita harus benar-benar tahu kapan API kita digunakan oleh pihak ketiga, tetapi tidak benar-benar ketika kode akhirnya dieksekusi di runtime.


0

Inilah salah satu contoh dari MDN yang menurut saya sangat membantu untuk memahami penggunaannya.

Dalam JavaScript, kami memiliki async functionsyang mengembalikan objek AsyncFunction . AsyncFunctionbukan objek global tetapi orang dapat mengambilnya dengan menggunakan constructorproperti dan menggunakannya.

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

// AsyncFunction constructor
var AsyncFunction = Object.getPrototypeOf(async function(){}).constructor

var a = new AsyncFunction('a', 
                          'b', 
                          'return await resolveAfter2Seconds(a) + await resolveAfter2Seconds(b);');

a(10, 20).then(v => {
  console.log(v); // prints 30 after 4 seconds
});

-1

Itu tidak perlu. Ini hanyalah salah satu dari banyak hal tradisional, yang dilakukan oleh para juara OOP untuk mencoba mengubah warisan prototipikal JavaScript menjadi warisan klasik. Satu-satunya hal yang mengikuti

Student.prototype.constructor = Student; 

tidak, adalah bahwa Anda sekarang memiliki referensi "konstruktor" saat ini.

Dalam jawaban Wayne, yang telah ditandai sebagai benar, Anda dapat melakukan hal yang persis sama dengan kode berikut

Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new this.constructor(this.name);
};  

dengan kode di bawah ini (ganti saja this.constructor dengan Person)

Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new Person(this.name);
}; 

Terima kasih Tuhan bahwa dengan purist puritan ES6 klasik dapat menggunakan operator asli bahasa seperti kelas, meluas dan super dan kita tidak harus melihat seperti koreksi prototipe. Konstruktor dan referensi orang tua.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.