Nội dung
- Tóm lại là
Thứ năm, 10/01/2019 | 00:00 GMT+7

Đố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;
}

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ả alligatorcrocprototype 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 

Tags:

Các tin liên quan

Thủ thuật với JavaScript Hủy cấu trúc
2018-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