Tôi đang cố gắng để hiểu đằng sau những bức màn của Javascript và bị mắc kẹt trong việc tìm hiểu việc tạo ra các đối tượng được xây dựng, đối tượng và chức năng đặc biệt và mối quan hệ giữa chúng.
Nó rất phức tạp, dễ hiểu lầm và rất nhiều cuốn sách Javascript mới bắt đầu đã sai, vì vậy đừng tin vào mọi thứ bạn đọc.
Tôi là một trong những người triển khai công cụ JS của Microsoft vào những năm 1990 và trong ủy ban tiêu chuẩn hóa, và tôi đã phạm một số sai lầm khi kết hợp câu trả lời này. (Mặc dù tôi đã không làm việc này trong hơn 15 năm nhưng có lẽ tôi có thể được tha thứ.) Đó là công cụ khó khăn. Nhưng một khi bạn hiểu kế thừa nguyên mẫu, tất cả đều có ý nghĩa.
Khi tôi đọc rằng tất cả các đối tượng được xây dựng như Array, String, v.v. đều là phần mở rộng (được kế thừa) từ Object, tôi cho rằng Object là đối tượng được xây dựng đầu tiên được tạo và phần còn lại của các đối tượng được thừa hưởng từ nó.
Bắt đầu bằng cách vứt bỏ mọi thứ bạn biết về kế thừa dựa trên lớp. JS sử dụng kế thừa dựa trên nguyên mẫu.
Tiếp theo, hãy chắc chắn rằng bạn có một định nghĩa rất rõ ràng trong đầu về "thừa kế" nghĩa là gì. Mọi người đã sử dụng các ngôn ngữ OO như C # hoặc Java hoặc C ++ nghĩ rằng kế thừa có nghĩa là phân nhóm, nhưng thừa kế không có nghĩa là phân nhóm. Kế thừa có nghĩa là các thành viên của một điều cũng là thành viên của một điều khác . Điều đó không nhất thiết có nghĩa là có một mối quan hệ phụ giữa những điều đó! Vì vậy, nhiều hiểu lầm trong lý thuyết loại là kết quả của việc mọi người không nhận ra rằng có một sự khác biệt.
Nhưng nó không có nghĩa gì khi bạn biết rằng các Đối tượng chỉ có thể được tạo bởi các hàm nhưng sau đó các hàm cũng không là gì ngoài các đối tượng của Hàm.
Điều này chỉ đơn giản là sai. Một số đối tượng không được tạo bằng cách gọi new F
cho một số chức năng F
. Một số đối tượng được tạo bởi thời gian chạy JS hoàn toàn không có gì. Có những quả trứng không được đẻ bởi bất kỳ con gà nào . Chúng chỉ được tạo bởi thời gian chạy khi nó khởi động.
Chúng ta hãy nói các quy tắc là gì và có thể sẽ giúp.
- Mỗi đối tượng có một đối tượng nguyên mẫu.
- Trong một số trường hợp nguyên mẫu có thể được
null
.
- Nếu bạn truy cập một thành viên trên một đối tượng và đối tượng không có thành viên đó, thì đối tượng sẽ chuyển sang nguyên mẫu của nó hoặc dừng lại nếu nguyên mẫu là null.
- Thành
prototype
viên của một đối tượng thường không phải là nguyên mẫu của đối tượng.
- Thay vào đó,
prototype
thành viên của một đối tượng hàm F là đối tượng sẽ trở thành nguyên mẫu của đối tượng được tạo bởi new F()
.
- Trong một số triển khai, các trường hợp có được một
__proto__
thành viên thực sự đưa ra nguyên mẫu của họ. (Điều này hiện không được chấp nhận. Đừng dựa vào nó.)
- Các đối tượng chức năng có được một đối tượng mặc định hoàn toàn mới được gán cho
prototype
khi chúng được tạo.
- Nguyên mẫu của một đối tượng chức năng là, tất nhiên
Function.prototype
.
Hãy tổng hợp lại.
- Nguyên mẫu của
Object
làFunction.prototype
Object.prototype
là đối tượng nguyên mẫu đối tượng.
- Nguyên mẫu của
Object.prototype
lànull
- Nguyên mẫu của
Function
là Function.prototype
- đây là một trong những tình huống hiếm hoi mà Function.prototype
thực sự là nguyên mẫu của Function
!
Function.prototype
là đối tượng nguyên mẫu hàm.
- Nguyên mẫu của
Function.prototype
làObject.prototype
Giả sử chúng ta tạo một hàm Foo.
- Nguyên mẫu của
Foo
là Function.prototype
.
Foo.prototype
là đối tượng nguyên mẫu Foo.
- Nguyên mẫu của
Foo.prototype
là Object.prototype
.
Giả sử chúng ta nói new Foo()
- Nguyên mẫu của đối tượng mới là
Foo.prototype
Hãy chắc chắn rằng có ý nghĩa. Hãy vẽ nó. Hình bầu dục là trường hợp đối tượng. Các cạnh có __proto__
nghĩa là "nguyên mẫu của" hoặc prototype
có nghĩa là " prototype
tài sản của".
Tất cả thời gian chạy phải làm là tạo tất cả các đối tượng đó và gán các thuộc tính khác nhau của chúng cho phù hợp. Tôi chắc rằng bạn có thể thấy điều đó sẽ được thực hiện như thế nào.
Bây giờ hãy xem một ví dụ kiểm tra kiến thức của bạn.
function Car(){ }
var honda = new Car();
print(honda instanceof Car);
print(honda.constructor == Car);
Cái này in cái gì?
Chà, instanceof
có nghĩa là gì? honda instanceof Car
có nghĩa là " Car.prototype
bằng với bất kỳ đối tượng nào trên honda
chuỗi nguyên mẫu?"
Vâng, đúng vậy. honda
Nguyên mẫu là Car.prototype
vậy, chúng ta đã hoàn thành. Điều này in đúng.
Còn cái thứ hai thì sao?
honda.constructor
không tồn tại vì vậy chúng tôi tham khảo nguyên mẫu, đó là Car.prototype
. Khi Car.prototype
đối tượng được tạo, nó sẽ tự động được cung cấp một thuộc tính constructor
bằng Car
, vì vậy điều này là đúng.
Bây giờ thì sao?
var Animal = new Object();
function Reptile(){ }
Reptile.prototype = Animal;
var lizard = new Reptile();
print(lizard instanceof Reptile);
print(lizard.constructor == Reptile);
Chương trình này in cái gì?
Một lần nữa, lizard instanceof Reptile
có nghĩa là "có Reptile.prototype
bằng với bất kỳ đối tượng nào trên lizard
chuỗi nguyên mẫu không?"
Vâng, đúng vậy. lizard
Nguyên mẫu là Reptile.prototype
vậy, chúng ta đã hoàn thành. Điều này in đúng.
Bây giờ thì sao
print(lizard.constructor == Reptile);
Bạn có thể nghĩ rằng điều này cũng in đúng, vì lizard
đã được xây dựng new Reptile
nhưng bạn sẽ sai. Lý do nó ra.
- Liệu
lizard
có một constructor
bất động sản? Không. Vì vậy, chúng tôi nhìn vào nguyên mẫu.
- Nguyên mẫu của
lizard
là Reptile.prototype
, đó là Animal
.
- Liệu
Animal
có một constructor
bất động sản? Không. Vì vậy, chúng tôi nhìn vào nguyên mẫu của nó.
- Nguyên mẫu của
Animal
là Object.prototype
, và Object.prototype.constructor
được tạo bởi thời gian chạy và bằng Object
.
- Vì vậy, điều này in sai.
Đáng lẽ chúng ta nên nói Reptile.prototype.constructor = Reptile;
vào một lúc nào đó trong đó, nhưng chúng ta không nhớ!
Hãy chắc chắn rằng tất cả có ý nghĩa với bạn. Vẽ một số hộp và mũi tên nếu vẫn còn khó hiểu.
Một điều cực kỳ khó hiểu khác là, nếu tôi console.log(Function.prototype)
in một chức năng nhưng khi tôi in console.log(Object.prototype)
nó sẽ in một đối tượng. Tại sao Function.prototype
một chức năng khi nó có nghĩa là một đối tượng?
Nguyên mẫu hàm được định nghĩa là một hàm mà khi được gọi sẽ trả về undefined
. Chúng tôi đã biết đó Function.prototype
là Function
nguyên mẫu, đủ kỳ lạ. Vì vậy, Function.prototype()
là hợp pháp, và khi bạn làm điều đó, bạn sẽ nhận undefined
lại. Vì vậy, nó là một chức năng.
Các Object
nguyên mẫu không có tài sản này; nó không thể gọi được Nó chỉ là một vật thể.
khi bạn console.log(Function.prototype.constructor)
lại là một chức năng.
Function.prototype.constructor
chỉ là Function
, rõ ràng. Và Function
là một chức năng.
Bây giờ làm thế nào bạn có thể sử dụng một cái gì đó để tự tạo ra nó (Mind = blown).
Bạn đang suy nghĩ quá mức này . Tất cả những gì được yêu cầu là thời gian chạy tạo ra một loạt các đối tượng khi nó khởi động. Các đối tượng chỉ là các bảng tra cứu liên kết các chuỗi với các đối tượng. Khi thời gian chạy khởi động, tất cả những gì phải làm là tạo ra một vài đối tượng chục trống, và sau đó bắt đầu giao prototype
, __proto__
, constructor
, và vân vân thuộc tính của từng đối tượng cho đến khi họ làm cho đồ thị mà họ cần để thực hiện.
Sẽ rất hữu ích nếu bạn lấy sơ đồ mà tôi đã đưa cho bạn ở trên và thêm constructor
các cạnh vào nó. Bạn sẽ nhanh chóng thấy rằng đây là một biểu đồ đối tượng rất đơn giản và thời gian chạy sẽ không có vấn đề gì khi tạo nó.
Một bài tập tốt sẽ là tự làm. Ở đây, tôi sẽ bắt đầu với bạn. Chúng ta sẽ sử dụng my__proto__
có nghĩa là "đối tượng nguyên mẫu của" và myprototype
có nghĩa là "thuộc tính nguyên mẫu của".
var myobjectprototype = new Object();
var myfunctionprototype = new Object();
myfunctionprototype.my__proto__ = myobjectprototype;
var myobject = new Object();
myobject.myprototype = myobjectprototype;
Và như thế. Bạn có thể điền vào phần còn lại của chương trình để xây dựng một tập hợp các đối tượng có cấu trúc liên kết tương tự như các đối tượng tích hợp Javascript "thực" không? Nếu bạn làm như vậy, bạn sẽ thấy nó cực kỳ dễ dàng.
Các đối tượng trong JavaScript chỉ là các bảng tra cứu liên kết các chuỗi với các đối tượng khác . Đó là nó! Không có phép thuật ở đây. Bạn đang bị trói buộc trong các nút thắt bởi vì bạn đang tưởng tượng các ràng buộc không thực sự tồn tại, giống như mọi đối tượng phải được tạo bởi một nhà xây dựng.
Các hàm chỉ là các đối tượng có khả năng bổ sung: được gọi. Vì vậy, đi qua chương trình mô phỏng nhỏ của bạn và thêm một thuộc .mycallable
tính cho mọi đối tượng cho biết liệu nó có thể gọi được hay không. Nó đơn giản như vậy.
Function.prototype
có thể là một hàm và có các trường bên trong. Vì vậy, không có bạn không thực hiện chức năng nguyên mẫu khi đi qua cấu trúc của nó. Cuối cùng hãy nhớ rằng có một công cụ diễn giải Javascript, vì vậy Object và Function có thể được tạo trong công cụ chứ không phải từ Javascript và tham chiếu đặc biệt nhưFunction.prototype
vàObject.prototype
có thể được công cụ hiểu theo cách đặc biệt.