Kỹ thuật mô phỏng lớp và kế thừa trong JavaScript
1. Lớp trong Javascript
Javascript thực tế là một thế giới của các hàm và các đối tượng, ban đầu nó được thiết kế một cách đơn giản, không có khái niệm về lớp một cách rõ ràng, có lẽ những người tạo ra Javascript cũng không thể nghĩ rằng có một ngày ngôn ngữ này lại được sử dụng một cách rộng rãi đến như vậy.
Đối tượng trong Javascript là một thực thể có nhiều cặp "Khóa/Giá trị", và bạn có thể truy cập vào các giá trị thông qua đối tượng và khóa.
object-example1.js
var tom = {
name: "Tom",
country: "USA"
};
// Access:
console.log( tom.name ); // Tom
console.log( tom.country ); // USA
console.log( tom["name"] ); // Tom
console.log( tom["country"] ); // USA
Bạn có thể thêm cặp "Khóa/Giá trị" mới vào cho một đối tượng có sẵn hoặc xóa bỏ một cặp "Khóa/Giá trị" nào đó của nó.
object-example2.js
var tom = {
name: "Tom",
country: "USA"
};
// Delete property - country
delete tom["country"]; // Same as: delete tom.country;
// Add property - gender
tom["gender"] = "Male"; // Same as: tom.gender = "Male";
// Access:
console.log( tom.name ); // Tom
console.log( tom["name"] ); // Tom
console.log( tom["country"] ); // undefined
console.log( tom.country ); // undefined
console.log( tom["gender"] ); // Male
console.log( tom.gender ); // Male
Lớp là một khái niệm hiện đại có trong các ngôn ngữ như Java, C#,... Lớp là một bản thiết kế, sử dụng bản thiết kế này giúp bạn nhanh chóng tạo ra các đối tượng có cấu trúc giống nhau. Phiên bản đầu tiên của Javascript không có khái niệm này.
Javascript ngày càng quan trọng vì vậy nó cần phải được nâng cấp, những nhà thiết kế Javascript cố gắng mô phỏng khái niệm Lớp dựa trên những khái niệm có sẵn trong Javascript. Cú pháp mô phỏng một lớp được giới thiệu trong ES3, ES5, nhưng phải đến ES6 chúng ta mới có một cú pháp hiện đại, và làm hài lòng tất cả mọi người.
Trước hết, để đơn giản hãy cùng tôi xem một cú pháp hiện đại được giới thiệu trong ECMAScript 6 để tạo ra lớp Rectangle (Hình chữ nhật), với 2 thuộc tính (property) width (chiều rộng) và height (chiều cao). Lớp này có phương thức getArea() trả về diện tích của hình chữ nhật này.
es6-class-example.js
// ECMAScript 6 class:
class Rectangle {
constructor (width , height) {
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
// ------------- TEST -------------------
var rect = new Rectangle(10, 5);
var area = rect.getArea();
console.log("Width: " + rect.width);
console.log("Height: " + rect.height);
console.log("Area: " + area);
Tạo ra một lớp con của một lớp thực sự rất dễ dàng với ES6:
es6-inheritance-example.js
class Shape {
constructor( x, y) {
this.setLocation(x, y);
}
setLocation(x, y) {
this.x = x;
this.y = y;
}
}
// Subclass:
class Circle extends Shape {
constructor(x, y, radius) {
// Call Shape's constructor via super
super(x, y);
this.radius = radius;
}
getArea() {
return Math.PI * this.radius * this.radius;
}
}
// ----- TEST ----
var circle = new Circle(0, 2, 5);
console.log( circle.getArea() );
ES6 đã giới thiệu một cú pháp hiện đại để tạo một lớp, và các lớp con, nhưng kỹ thuật thực sự không thay đổi. Cú pháp của ES6 đã che đi các khái niệm khó hiểu của Javascript khi nó cố gắng mô phỏng một lớp. Trong bài học này tôi thảo luận với bạn cách mà ES3 & ES5 đã làm để mô phỏng một lớp và mô phỏng sự thừa kế.
2. Lớp và thừa kế trong ES3
ES3 sử dụng từ khóa function để định nghĩa một "hàm tạo đối tượng". Bạn sẽ tạo ra một đối tượng mới khi bạn gọi hàm này với toán tử new:
// ECMAScript 3 class.
function Rectangle(width, height) {
this.width = width;
this.height = height;
}
// Create an Object:
var rect = new Rectangle(10, 5);
Hình dưới đây minh họa hành động được thực hiện bởi bộ máy thực thi Javascript:
- Đối tượng 'rect' được tạo ra (nó chỉ là một đối tượng thông thường, không có gì đặc biệt)
- Thêm thuộc tính (property) __proto__ cho đối tượng 'rect', đây là một thuộc tính ẩn.
- Biến this sẽ được trỏ tới địa chỉ của đối tượng 'rect'.
- Thêm thuộc tính (property) width cho đối tượng 'rect'.
- Thêm thuộc tính (property) height cho đối tượng 'rect'.
Khái niệm prototype cũng được giới thiệu trong ES3. Chúng ta hãy xem mục đích của nó là gì?
// ECMAScript 3 class.
function Rectangle(width, height) {
this.width = width;
this.height = height;
}
Rectangle.prototype.bgColor = "red";
Rectangle.prototype.borderColor = "blue";
// Create an Object:
var rect = new Rectangle(10, 5);
console.log(rect); // Rectangle { width: 10, height: 5 }
console.log(rect.__proto__); // Rectangle { bgColor: 'red',borderColor: 'blue' }
console.log(rect.__proto__.bgColor); // red
console.log(rect.__proto__.borderColor); // blue
// (Read the explanation**)
console.log(rect.bgColor); // red
console.log(rect.borderColor); // blue
Điều gì xẩy ra khi bộ máy thực thi Javascript bắt gặp biểu thức myObject.myProperty?
Câu trả lời là: Nó sẽ kiểm tra xem đối tượng myObject có thuộc tính (property) myProperty hay không, nếu có nó truy cập vào thuộc tính này, ngược lại nó truy cập vào myObject.__proto__.myProperty.
es3-proto-example3.js
var rect = {
__proto__ : { bgColor : "red", borderColor : "blue" },
width: 10,
height: 5
}
console.log(rect.width); // 10
console.log(rect.__proto__.bgColor); // red
console.log(rect.bgColor); // red
new AFunction(args) vs AFunction.call(anObj, args)
Thừa kế
Với ES3 bạn có thể mô phỏng một lớp thừa kế một lớp khác bằng nhiều cách khác nhau, tất nhiên nó không dễ dàng như cách bạn làm trong ES6, và nó khá khó hiểu với nhiều lập trình viên.
Để đơn giản tôi đưa ra một ví dụ về thừa kế, và phân tích nguyên tắc hoạt động của bộ máy thực thi Javascript trong trường hợp này.
- Lớp Animal có 2 thuộc tính (property) name & gender.
- Lớp Cat thừa kế từ lớp Animal, nó có 1 thuộc tính color.
es3-inheritance-example1.js
function Animal(n, g) {
this.name = n;
this.gender = g;
}
function Cat(n, g, c) {
Animal.call(this, n, g);
this.color = c;
}
var tom = new Cat("Male", "Tom", "Black");
// Cat { gender: 'Male', name: 'Tom', color: 'Black' }
console.log(tom);
Thừa kế và vai trò của prototype (ES3)
es3-inheritance-example2.js
// Class: Animal
function Animal(n, g) {
this.name = n;
this.gender = g;
}
Animal.prototype.sleep = function() {
console.log("Animal sleeping..");
}
Animal.prototype.move = function() {
console.log("Animal moving..");
}
// Class: Cat
function Cat(n, g, c) {
Animal.call(this, n, g); // IMPORTANT!!
this.color = c;
}
// IMPORTANT!!
var TempFunc = function() {}; // Temporary class.
TempFunc.prototype = Animal.prototype;
Cat.prototype = new TempFunc();
// ------------------
Cat.prototype.cry = function() {
console.log("Meo meo");
}
// Override 'move' method of Animal.
Cat.prototype.move = function() {
console.log("Cat moving..");
}
var tom = new Cat("Male", "Tom", "Black");
// Cat { gender: 'Male', name: 'Tom', color: 'Black' }
console.log(tom);
tom.move(); // Cat moving..
tom.sleep(); // Animal sleeping..
tom.cry(); // Meo meo
3. Lớp và thừa kế trong ES5
Object.create(srcObject)
Phương thức Object.create(srcObject) tạo ra một đối tượng mới newObject, trong đó newObject.__proto__ là bản sao chép của srcObject.
method-object-create-example.js
var john = {
name: "John",
gender: "Male"
};
var other = Object.create(john);
console.log(other.__proto__); // { name: 'John', gender: 'Male' }
Thừa kế (ES5 + Object.create)
Sử dụng phương thức Object.create(srcObject) của ES5 giúp bạn mô phỏng lớp và sự thừa kế dễ dàng hơn so với ES3.
es5-inheritance-example1.js
// Class: Animal
function Animal(n, g) {
this.name = n;
this.gender = g;
}
Animal.prototype.sleep = function() {
console.log("Animal sleeping..");
}
Animal.prototype.move = function() {
console.log("Animal moving..");
}
// Class: Cat
function Cat(n, g, c) {
Animal.call(this, n, g); // IMPORTANT!!
this.color = c;
}
Cat.prototype = Object.create(Animal.prototype); // IMPORTANT!!
Cat.prototype.cry = function() {
console.log("Meo meo");
}
// Override 'move' method of Animal.
Cat.prototype.move = function() {
console.log("Cat moving..");
}
var tom = new Cat("Male", "Tom", "Black");
// Cat { gender: 'Male', name: 'Tom', color: 'Black' }
console.log(tom);
tom.move(); // Cat moving..
tom.sleep(); // Animal sleeping..
tom.cry(); // Meo meo
Các hướng dẫn ECMAScript, Javascript
- Giới thiệu về Javascript và ECMAScript
- Bắt đầu nhanh với Javascript
- Hộp thoại Alert, Confirm, Prompt trong Javascript
- Bắt đầu nhanh với JavaScript
- Biến (Variable) trong JavaScript
- Các toán tử Bitwise
- Mảng (Array) trong JavaScript
- Vòng lặp trong JavaScript
- Hàm trong JavaScript
- Hướng dẫn và ví dụ JavaScript Number
- Hướng dẫn và ví dụ JavaScript Boolean
- Hướng dẫn và ví dụ JavaScript String
- Câu lệnh rẽ nhánh if/else trong JavaScript
- Câu lệnh rẽ nhánh switch trong JavaScript
- Hướng dẫn xử lý lỗi trong JavaScript
- Hướng dẫn và ví dụ JavaScript Date
- Hướng dẫn và ví dụ JavaScript Module
- Lịch sử phát triển của module trong JavaScript
- Hàm setTimeout và setInterval trong JavaScript
- Hướng dẫn và ví dụ Javascript Form Validation
- Hướng dẫn và ví dụ JavaScript Web Cookie
- Từ khóa void trong JavaScript
- Lớp và đối tượng trong JavaScript
- Kỹ thuật mô phỏng lớp và kế thừa trong JavaScript
- Thừa kế và đa hình trong JavaScript
- Tìm hiểu về Duck Typing trong JavaScript
- Hướng dẫn và ví dụ JavaScript Symbol
- Hướng dẫn JavaScript Set Collection
- Hướng dẫn JavaScript Map Collection
- Tìm hiểu về JavaScript Iterable và Iterator
- Hướng dẫn sử dụng biểu thức chính quy trong JavaScript
- Hướng dẫn và ví dụ JavaScript Promise, Async Await
- Hướng dẫn và ví dụ Javascript Window
- Hướng dẫn và ví dụ Javascript Console
- Hướng dẫn và ví dụ Javascript Screen
- Hướng dẫn và ví dụ Javascript Navigator
- Hướng dẫn và ví dụ Javascript Geolocation API
- Hướng dẫn và ví dụ Javascript Location
- Hướng dẫn và ví dụ Javascript History API
- Hướng dẫn và ví dụ Javascript Statusbar
- Hướng dẫn và ví dụ Javascript Locationbar
- Hướng dẫn và ví dụ Javascript Scrollbars
- Hướng dẫn và ví dụ Javascript Menubar
- Hướng dẫn xử lý JSON trong JavaScript
- Xử lý sự kiện (Event) trong Javascript
- Hướng dẫn và ví dụ Javascript MouseEvent
- Hướng dẫn và ví dụ Javascript WheelEvent
- Hướng dẫn và ví dụ Javascript KeyboardEvent
- Hướng dẫn và ví dụ Javascript FocusEvent
- Hướng dẫn và ví dụ Javascript InputEvent
- Hướng dẫn và ví dụ Javascript ChangeEvent
- Hướng dẫn và ví dụ Javascript DragEvent
- Hướng dẫn và ví dụ Javascript HashChangeEvent
- Hướng dẫn và ví dụ Javascript URL Encoding
- Hướng dẫn và ví dụ Javascript FileReader
- Hướng dẫn và ví dụ Javascript XMLHttpRequest
- Hướng dẫn và ví dụ Javascript Fetch API
- Phân tích XML trong Javascript với DOMParser
- Giới thiệu về Javascript HTML5 Canvas API
- Làm nổi bật Code với thư viện Javascript SyntaxHighlighter
- Polyfill là gì trong khoa học lập trình?
Show More