TLDR; Không phải là siêu cần thiết, nhưng có thể sẽ giúp về lâu dài, và chính xác hơn là làm như vậy.
LƯU Ý: Đã chỉnh sửa nhiều vì câu trả lời trước của tôi được viết một cách khó hiểu và có một số lỗi mà tôi đã bỏ qua khi vội vàng trả lời. Cảm ơn những người đã chỉ ra một số lỗi nghiêm trọng.
Về cơ bản, đó là kết nối phân lớp chính xác trong Javascript. Khi chúng ta phân lớp, chúng ta phải thực hiện một số điều thú vị để đảm bảo rằng ủy nhiệm nguyên mẫu hoạt động chính xác, bao gồm ghi đè một prototype
đối tượng. Ghi đè một prototype
đối tượng bao gồm constructor
, do đó chúng ta cần sửa tham chiếu.
Hãy nhanh chóng xem xét cách 'các lớp' trong ES5 hoạt động.
Giả sử bạn có một hàm tạo và nguyên mẫu của nó:
//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',
}
Khi bạn gọi hàm tạo để khởi tạo, hãy nói Adam
:
// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);
Các new
từ khóa gọi với 'Người' về cơ bản sẽ chạy constructor Person với một vài dòng bổ sung mã:
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
* }
*/
Nếu chúng ta console.log(adam.species)
, việc tra cứu sẽ thất bại trong adam
trường hợp đó, và tìm kiếm chuỗi nguyên mẫu cho nó .prototype
, đó là Person.prototype
- và Person.prototype
có một .species
tài sản, vì vậy việc tra cứu sẽ thành công Person.prototype
. Sau đó nó sẽ đăng nhập 'human'
.
Ở đây, Person.prototype.constructor
sẽ chính xác chỉ đến Person
.
Vì vậy, bây giờ là phần thú vị, cái gọi là 'phân lớp'. Nếu chúng tôi muốn tạo một Student
lớp, đó là một lớp con của Person
lớp với một số thay đổi bổ sung, chúng tôi sẽ cần đảm bảo rằng các Student.prototype.constructor
điểm cho Sinh viên chính xác.
Nó không tự làm điều này. Khi bạn phân lớp, mã trông như thế này:
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: {}
Gọi new Student()
ở đây sẽ trả về một đối tượng với tất cả các thuộc tính chúng ta muốn. Ở đây, nếu chúng tôi kiểm tra eve instanceof Person
, nó sẽ trở lại false
. Nếu chúng ta cố gắng truy cập eve.species
, nó sẽ trở lại undefined
.
Nói cách khác, chúng ta cần kết nối ủy quyền để eve instanceof Person
trả về giá trị true và sao cho các trường hợp Student
ủy nhiệm chính xác Student.prototype
, và sau đó Person.prototype
.
NHƯNG vì chúng ta đang gọi nó bằng new
từ khóa, hãy nhớ những gì mà lời gọi đó thêm vào? Nó sẽ gọi Object.create(Student.prototype)
, đó là cách chúng tôi thiết lập mối quan hệ ủy thác giữa Student
và Student.prototype
. Lưu ý rằng ngay bây giờ, Student.prototype
là trống rỗng. Vì vậy, nhìn lên .species
một thể hiện của Student
sẽ thất bại vì nó đại biểu duy nhất Student.prototype
, và các .species
tài sản không tồn tại trên Student.prototype
.
Khi chúng ta làm assign Student.prototype
để Object.create(Person.prototype)
, Student.prototype
bản thân sau đó các đại biểu đến Person.prototype
, ngước mắt lên eve.species
sẽ trở lại human
như chúng ta mong đợi. Có lẽ chúng tôi muốn nó kế thừa từ Student.prototype AND Person.prototype. Vì vậy, chúng ta cần khắc phục tất cả điều đó.
/* 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);
Bây giờ phái đoàn làm việc, nhưng chúng tôi ghi đè Student.prototype
bằng một Person.prototype
. Vì vậy, nếu chúng ta gọi Student.prototype.constructor
, nó sẽ chỉ đến Person
thay vì Student
. Đây là lý do tại sao chúng ta cần sửa nó.
// 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
Trong ES5, thuộc tính của chúng tôi constructor
là một tham chiếu đề cập đến một chức năng mà chúng tôi đã viết với mục đích trở thành một 'nhà xây dựng'. Ngoài những gì new
từ khóa mang lại cho chúng ta, thì hàm tạo còn là một hàm 'đơn giản'.
Trong ES6, constructor
giờ đây được xây dựng theo cách chúng ta viết các lớp - như trong, nó được cung cấp như một phương thức khi chúng ta khai báo một lớp. Đây chỉ đơn giản là đường cú pháp nhưng nó phù hợp với chúng ta một số tiện ích như truy cập vào super
khi chúng ta đang mở rộng một lớp hiện có. Vì vậy, chúng tôi sẽ viết mã ở trên như thế này:
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;
}
}