- vừa được xem lúc

Prototype-based programming trong JavaScript

0 0 8

Người đăng: Thavrith

Theo Viblo Asia

Giới thiệu

Trước (và thậm chí sau khi) giới thiệu phiên bản ES2015, việc triển khai OOP trong JavaScript dựa vào prototype-based programming. Trong phong cách lập trình này, object tự đóng gói các properties, methods và data của nó.

Bạn có thể thêm các properties mới cho object này bất cứ khi nào bạn muốn. Object này là độc lập/ riêng lẻ, thay vì là một thể hiện (instance) của một class nào đó; với cách này, nếu bạn muốn một object, bạn có thể tạo một object mà không cần tạo class trước.

JavaScript Prototypes

Tất cả các JavaScript objects đều có property prototype: __proto__. Prototype của mỗi object được gán cho object trong quá trình tạo ra object đó, nhìn ví dụ sau đây:

let vehicle = { wheels : 4 }; let car = { seats : 5 }; let driver = {} 

In tất cả objects và property __proto__ của mỗi variable:

console.log(`vehicle:`, vehicle, vehicle.__proto__);
console.log(`car:`, car, car.__proto__);
console.log(`driver:`, driver, driver.__proto__);

Output

vehicle: { wheels: 4 } {}
car: { seats: 5 } {}
driver: {} {}

Property __proto__ của mỗi object có thể được coi là một empty object vì các properties của prototype object bị ẩn. Nhưng nó có thể được nhìn thấy thông qua một phương tiện khác, chẳng hạn như browser console. Dưới đây ta sẽ xem các properties được gán cho variable vehicle:

Tất cả các properties này đềucần thiết cho việc phân loại object và triển khai OOP trong JavaScript.

Sử dụng prototypes

let vehicle = { wheels : 4 }; let car = { seats : 5, __proto__ : vehicle
}; console.log(`vehicle:`, vehicle, vehicle.__proto__);
console.log(`car:`, car, car.__proto__);
console.log(`vehicle seat:`,vehicle.seats);
console.log(`car wheels:`, car.wheels);

Output

vehicle: { wheels: 4 } {}
car: { seats: 5 } { wheels: 4 }
vehicle seat: undefined
car wheels: 4

Đoạn mã trên giống như ví dụ trước của chúng ta, object được gán cho variable car, thêm cái là bây giờ chúng ta gán vehicle cho property __proto__ của biến car.

Khi chúng ta in thuộc tính seats (thuộc tính của object car) mà truy cập từ biến vehicle, chúng ta nhận được giá trị không xác định (undefined).

Tuy nhiên, khi chúng ta in thuộc tính wheels (thuộc tính của vehicle) truy cập từ biến car, chúng ta nhận được 4.

Điều này là do vehicle đã được gán cho __proto__ của đối tượng car .

Việc gán vehicle cho __proto__ của car cho phép object car truy cập các properties của object vehicle

Property __proto__ cho phép truy cập các thuộc tính của một object khác, nếu object đó được gán cho nó.

Vậy nếu object car có một property tên là wheels (trùng tên với property của object được gán cho __proto__ của nó) thì sao?

let vehicle = { wheels : 4 }; let car = { seats : 5, __proto__ : vehicle, wheels : 6,
}; console.log(`vehicle:`, vehicle, vehicle.__proto__);
console.log(`car:`, car, car.__proto__);
console.log(`car wheels:`, car.wheels);

Output

vehicle: { wheels: 4 } {}
car: { seats: 5, wheels: 6 } { wheels: 4 }
car wheels: 6

Với đoạn mã trên, chúng ta có thể thấy rằng mặc dù __proto__ được gán cho car, giá trị car.wheels6 vì object car có property wheels của riêng nó. Property __proto__ chỉ được kiểm tra nếu đối tượng car không có property có tên tương ứng.

Truy cập và thay đổi prototypes

Vậy, chúng ta đã tìm hiểu về property __proto__ và đã thử gán cũng như truy cập trực tiếp vào property đó. Các phép gán hoặc truy xuất các objects prototype của một object gọi ngầm hai phương thức sau:

  • Object.getPrototypeOf(obj): Lấy giá trị thuộc tính __proto__ của object obj
  • Object.setPrototypeOf(obj, proto): Gán giá trị của proto cho thuộc tính __proto__ của object obj

Giờ ta thử sử dụng trực tiếp 2 methods này thay vì gán như ở trên:

let vehicle = { wheels : 4 }; let car = { seats : 5 }; Object.setPrototypeOf(car, vehicle); console.log(`vehicle:`, vehicle, Object.getPrototypeOf(vehicle));
console.log(`car:`, car, Object.getPrototypeOf(car));
console.log(`car wheels:`, car.wheels);

Output

vehicle: { wheels: 4 } {}
car: { seats: 5 } { wheels: 4 }
car wheels: 4

Trong đoạn mã trên, sử dụng các methods Object.getPrototypeOfObject.setPrototypeOf cho kết quả tương tự khi ta gán object cho __proto__ của một object khác.

JavaScript Constructor Functions

Chúng ta sẽ sử dụng các functions để xây dựng các objects, còn được gọi là constructor functions. Đây là cách tiếp cận tốt nhất để triển khai OOP dựa trên prototype.

Constructor functions

Các constructor functions hoặc, object constructor functions chứa các bản thiết kế bao gồm các properties và methods cho một loại đối tượng nào đó, và constructor function này dùng để tạo các đối tượng. Tất cả các đối tượng được tạo từ constructor function sẽ có các properties và methods giống nhau nhưng không nhất thiết phải có cùng giá trị.

Cú pháp

function FunctionName(parameter1, parameter2,...){ // các properties của đối tượng được khởi tạo ở đây // các methods được cung cấp cho các đối tượng được định nghĩa ở đây
}

LƯU Ý: Các tham số là tùy chọn.

Ví dụ


// Tên của constructor function: Employee
function Employee(_name, _age, _designation){ // Các properties của đối tượng được tạo được gán bởi các tham số được truyền vào. this.name = _name; this.age = _age; this.designation = _designation; // Cập nhật giá trị của property age  this.setAge = newage => { console.log(`setting age from ${this.age} to ${newage}`) this.age = newage; } this.company = 'Amazon';
} var employee1 = new Employee('Mark', 12, 'Manager'); console.log(`employee1 name: ${employee1.name} age: ${employee1.age}`)
console.log(`employee1 company: ${employee1.company}`) employee1.setAge(20); console.log(`employee1 name: ${employee1.name} age: ${employee1.age}`)

Output

employee1 name: Mark age: 12
employee1 company: Amazon
setting age from 12 to 20
employee1 name: Mark age: 20

Ở đoạn code trên, ta đã làm quen với việc tạo ra constructor function có chứa properties, method, sau đó tạo object bằng constructor function và truy cập properties của objects đó.

Vậy thêm property sau khi constructor function đã được tạo ra thì sao?


function Employee(_name, _age, _designation){ this.name = _name; this.age = _age; this.designation = _designation; this.setAge = newage => { console.log(`setting age from ${this.age} to ${newage}`) this.age = newage; } this.company = 'Amazon';
} Employee.planet = 'Earth'; // thử thêm property planet vào constructor function Employee var employee1 = new Employee('Mark', 20, 'Manager'); var employee2 = new Employee('Bob', 30, 'Accountant'); employee1.gender = 'male'; // thêm property gender cho employee1 console.log(`employee names: ${employee1.name}, ${employee2.name}`)
console.log(`employee planet: ${employee1.planet}, ${employee2.planet}`)
console.log(`employee gender: ${employee1.gender}, ${employee2.gender}`)

Output

employee names: Mark, Bob
employee planet: undefined, undefined
employee gender: male, undefined
  • Ở dòng Employee.planet = 'Earth', ta đang cố thêm property planet vào vào constructor function , điều này là không được.
  • Nhưng với dòng employee1.gender = 'male';, ta thêm property gender cho employee1, và được.

Ta thấy, khi truy cập planet từ 2 object employee1employee2, ta nhận được undefined, nhưng khi thử truy cập property gender của employee1 thì lại nhận được male. JS cho phép chúng ta thêm property cho các object đã tạo mà không làm thay đổi property của các object khác.

Prototype objects với constructor functions

Tất cả các objects được tạo bởi constructor functions chia sẻ prototype object của nó. Lấy ví dụ trước của chúng ta, tất cả các object được tạo bằng constructor function Employee sẽ chia sẻ cùng một prototype object.

Thử xem đoạn code sau đây:

function Employee(_name, _age, _designation){ this.name = _name; this.age = _age; this.designation = _designation; this.setAge = newage => { console.log(`setting age from ${this.age} to ${newage}`) this.age = newage; } this.company = 'Amazon';
}
Employee.prototype.name = 'Bill';
var employee1 = new Employee('Mark', 20, 'Manager'); var employee2 = new Employee('Bob', 30, 'Accountant'); console.log(employee1.name, employee2.name);
console.log(Employee.prototype);
console.log(`employee protoypes: ${employee1.__proto__}, ${employee2.__proto__}`);
console.log(`protoype equalities: ${employee1.__proto__ === employee2.__proto__}`);

Output

Mark Bob
Employee { name: 'Bill' }
employee protoypes: [object Object], [object Object]
protoype equalities: true

Prototype object của constructor function Employee được gán cho __proto__ của mỗi đối tượng được tạo ra bởi constructor function Employee.

Đó là lý do khi ta so sánh __proto__ của employee1employee2, ta nhận được kết quả là chúng giống nhau (true).

Và prototype object của constructor function Employee được truy cập như sau:

Employee.prototype

Nên nếu chúng ta so sánh

console.log(`protoype equalities: ${Employee.prototype === employee2.__proto__}`);

Cũng sẽ nhận được kết quả là true.

protoype equalities: true

Kết bài

Vậy là em/mình đã giới thiệu qua prototypes và constructor functions trong JavaScript. Bài này là bài đầu tiên trong series ngắn: OOP trong JavaScript.

Bài này được viết lúc em/mình đang học JavaScript nên nếu có gì sai sót cũng như hiểu chưa tới, mong mọi người góp ý và chỉnh sửa. Cảm ơn mọi người đã đọc.

Em vừa tốt nghiệp, đang làm việc intern/fresher SWE/Python developer/JS Developer. Mong team anh chị nào cần người có thể cân nhắc cho em 1 cơ hội. Em cảm ơn.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Chương 5 Object oriented programming

Chương 5 Object oriented programming. Tôi lần đầu tiên được giới thiệu về lập trình hướng đối tượng ở trường cao đẳng nơi tôi đã có một giới thiệu tóm tắc về c++.

0 0 24

- vừa được xem lúc

SOLID trong OOP và ví dụ dễ hiểu bằng Python

Thế SOLID là gì? SOLID là cứng . Đùa tí Đây là các nguyên lý thiết kế trong OOP, được ghép lại từ các chữ cái đầu của Single Responsibility, Open Close Principle, Liskov Substitution Principle, Interf

0 0 27

- vừa được xem lúc

002: Object và Class trong OOP

Bài viết nằm trong series Object-Oriented Design from real life to software. Về mặt ý tưởng, OOP nói đến việc áp dụng từ thế giới thực vào thế giới lập trình.

0 0 30

- vừa được xem lúc

001: Procedural programming và Object-Oriented programming

Bài viết nằm trong series Object-Oriented Design from real life to software. 1) Procedural programming.

0 0 31

- vừa được xem lúc

003: Các tính chất cơ bản trong OOP P1

Bài viết nằm trong series Object-Oriented Design from real life to software. . . Abstraction.

0 0 41

- vừa được xem lúc

004: Các tính chất cơ bản trong OOP P2

Bài viết nằm trong series Object-Oriented Design from real life to software. . . Inheritance.

0 0 37