Đối tượng, Nguyên mẫu và Lớp trong JavaScript
Xem xét thực tế rằng hầu hết mọi thứ trong JavaScript đều là một đối tượng, mã JavaScript hướng đối tượng rất khác với các ngôn ngữ hỗ trợ đối tượng khác. Hệ thống đối tượng JS thay vào đó là một hệ thống đối tượng dựa trên nguyên mẫu.
Xuất thân từ một nền C ++, tôi đã nhận thức được sự Object Oriented mô hình lập trình và đã có một ý tưởng rất cứng nhắc về cách đối tượng và lớp nên làm việc. Tiếp xúc với các ngôn ngữ khác như Java dường như chỉ để cài đặt thêm ý tưởng này. Trong khi các ngôn ngữ này có ngữ nghĩa riêng về cách các đối tượng và lớp hoạt động; Javascript, đối với user mới, là một điều thú vị.
Trước hết, các đối tượng JavaScript rất khác nhau về cách chúng được tạo ra. Không có yêu cầu cho một lớp học. Các thể hiện đối tượng có thể được tạo bằng toán tử mới :
let Reptile = new Object() {
// ...
}
hoặc với một hàm tạo hàm
function Reptile() {
// ...
}
Thứ hai, các đối tượng JavaScript rất linh hoạt. Trong khi các ngôn ngữ hướng đối tượng cổ điển chỉ cho phép sửa đổi thuộc tính hoặc các vị trí thuộc tính, JavaScript cho phép các đối tượng sửa đổi các thuộc tính và phương thức của chúng; tức là các đối tượng JavaScript có cả vùng thuộc tính và phương thức.
Suy nghĩ đầu tiên của tôi khi khám phá là "vâng tự do!" nhưng điều này đi kèm với chi phí - cần phải hiểu thuộc tính nguyên mẫu trong JavaScript. Kiến thức về nguyên mẫu là điều cần thiết đối với một nhà phát triển muốn triển khai bất kỳ hình thức nào của hệ thống hướng đối tượng trong JavaScript.
Tất cả các đối tượng JavaScript được tạo từ phương thức khởi tạo Object
:
var Reptile = function(name, canItSwim) {
this.name = name;
this.canItSwim = canItSwim;
}
Và prototype
cho phép ta thêm các phương thức mới vào các hàm tạo đối tượng, điều này nghĩa là phương thức sau hiện tồn tại trong tất cả các trường hợp của Reptile
.
Reptile.prototype.doesItDrown = function() {
if (this.canItSwim) {
console.log(`${this.name} can swim`);
} else {
console.log(`${this.name} has drowned`);
}
};
Các version đối tượng của Reptile
có thể được tạo:
// for this example consider alligators can swim and crocs cannot
let alligator = new Reptile("alligator", true);
alligator.doesItDrown(); // alligator can swim
let croc = new Reptile("croc", false);
croc.doesItDrown(); // croc has drowned
prototype
của đối tượng Reptile
hiện là cơ sở để kế thừa, phương thức doesItDrown
có thể truy cập được cho cả alligator
và croc
vì prototype
của Reptile
có phương thức này. Thuộc tính prototype
được chia sẻ giữa tất cả các version của nó và có thể truy cập được thông qua thuộc tính __proto__
của một version cụ thể.
Bây giờ, do sự tồn tại của các khe phương thức và một thuộc tính phiên prototype
chung được chia sẻ trên tất cả các version , một số thủ thuật rất gọn gàng có thể xảy ra mà rất kỳ lạ đối với dân C ++:
croc.__proto__.doesItDrown = function() {
console.log(`the croc never drowns`);
};
croc.doesItDrown(); // the croc never drowns
alligator.doesItDrown(); // the croc never drowns
Thay đổi thuộc tính hoặc phương thức prototype
của một đối tượng, tất cả các version của đối tượng đều bị ảnh hưởng. Điều này nghĩa là ta cũng có thể xóa nội dung. Một kẻ mệt mỏi vì chết đuối có thể làm điều này:
delete croc.__proto__.doesItDrown
alligator.doesItDrown();
//TypeError: alligator.doesItDrown
// is not a function
Bây giờ không ai được bơi.
Đây chỉ là một ví dụ ngớ ngẩn cho thấy prototype
cơ bản như thế nào đối với hệ thống Đối tượng trong JavaScript và nó có thể khá chói tai đối với những người từ các ngôn ngữ hướng đối tượng khác.
Với cú pháp ES6, JavaScript đã được cung cấp tính năng tạo các lớp.
Tuy nhiên, khái niệm lớp thực sự không tồn tại trong JavaScript mà nó được mô phỏng thông qua prototype
và cú pháp của lớp chỉ là đường cú pháp xung quanh nó. Do đó, hiểu được hành vi này là quan trọng để nhận ra sự tiện lợi và hạn chế của các lớp ES6.
Với cú pháp class
mới, Reptile
sẽ được định nghĩa là:
class Reptile {
constructor (name, canItSwim) {
this.name = name;
this.canItSwim = canItSwim;
}
doesItDrown () {
if(this.canItSwim)
console.log(`${this.name} can swim`);
else
console.log(`${this.name} has drowned`);
}
}
let alligator = new Reptile("alligator", true);
alligator.doesItDrown(); //alligator can swim
Điều này không nghĩa là nó không mang lại điều gì mới mẻ đối với ưu đãi dành cho user prototype
, một số cạm bẫy có thể tránh được bằng cách sử dụng các lớp ES6, chẳng hạn như đặt từ khóa new
bắt buộc để tạo version .
let croc = Reptile("croc", false);
//TypeError: Class constructor Reptile cannot be invoked without 'new'
Đây thực sự là một điều tốt, vì nó ngăn chặn việc truy cập sai ngữ cảnh khi sử dụng các thuộc tính và phương thức đối tượng, thường là phạm vi toàn cục hoặc đối tượng cửa sổ.
Tóm lại là
Mặc dù JavaScript ngay bây giờ chắc chắn thiếu các tính năng như các thành viên thực sự riêng tư. Nó đã thực hiện việc tạo các đối tượng thông qua cú pháp lớp, thay vì các nguyên mẫu gần giống với các lớp từ các ngôn ngữ OO khác như C ++ / Java.
Tái bút. Đã có đề xuất với TC39 về việc tạo thành viên thực sự riêng tư trong các lớp JavaScript, bạn có thể theo dõi tại đây và đóng góp ý kiến của bạn . Nếu nó được đưa vào bản sửa đổi tiếp theo, thì ta sẽ có những thứ như sau:
class Foo {
#a; #b; // # indicates private members here
#sum = function() { return #a + #b; };
}
// personally this format reminds me of $variable in PHP.
// I'm not sure if that's a good thing
Các tin liên quan
Thủ thuật với JavaScript Hủy cấu trúc2018-11-26
Đừng sợ theo dõi JavaScript
2018-10-17
Làm phẳng mảng trong Vanilla JavaScript với flat () và flatMap ()
2018-09-28
Cách sử dụng các phương thức đối tượng trong JavaScript
2018-08-03
Xử lý lỗi trong JavaScript Sử dụng try ... catch
2018-08-03
Hiểu sự kiện trong JavaScript
2018-06-19
Lập lịch tác vụ trong JavaScript Sử dụng setTimeout & setInterval
2018-06-12
Hiểu các lớp trong JavaScript
2018-05-04
Truy cập API Rails trong ứng dụng khách JavaScript bằng Rails Ranger
2018-03-15
Hiểu các biến, phạm vi và lưu trữ trong JavaScript
2018-02-20