Tổng hợp về hướng đối tượng của Javascript

Nguồn: http://ruby-rails.hatenadiary.com/entry/20150311/1426062668

Người dịch: Phạm Cẩm Anh

Tác giả là người đã có kinh nghiệm làm các ngôn ngữ hướng đối tượng như Java và PhP từ trước. Bài viết này tác giả muốn giới thiệu đến cho những người chưa hiểu rõ Javascript về hướng đối tượng đặc biệt của Javascript.

1. Tạo object

Có thể dùng cặp dấu {} để tạo object.

Ngoài ra cũng có thể dùng toán tử new để tạo một object xác định.

// Cách 1: Tạo object instance bằng {}
var obj = {};

// Cách 2: tạo instance bằng new
var obj = new Object();    // Object instance
var person = new Person(); // Person instance

2. Định nghĩa và gán thuộc tính

Nếu ta set giá trị property cho object, ta có thể set giá trị và định nghĩa property.

var obj = {};
// Nếu property chưa được định nghĩa, sẽ trả về undefined
console.log(obj.prop); // => undefined

// Khi set giá trị cho property, cũng đồng thời định nghĩa property
obj.prop = 1;
console.log(obj.prop); // => 1
Khi tạo object, ta có thể thiết lập, định nghĩa property
// Có thể định nghĩa, thiết lập property
var obj2 = {
  prop:  1,
  prop2: 'foo'
};

console.log(obj2.prop);  // => 1
console.log(obj2.prop2); // => 'foo'

3. Xóa property

Có thể xóa property dùng toán tử delete Sau khi xóa property, nếu dùng property này, sẽ trả về undefined giống như khi property chưa được định nghĩa.

var obj = {};
obj.prop = 1;

// Xóa property
delete obj.prop;

// Khi tham chiếu property, trả về undefined
console.log(obj.prop); // => undefined

Đừng xóa property bằng bằng cách gán property bằng undefined vì nếu làm thế thì khi sử dụng vòng lặp for list ra danh sách cá property, sẽ có cả property được set là undefined. Vì thế hãy dùng toán tử delete để xóa property.

4. Accessor property (getter/ setter)

Có thể định nghĩa accessor property bằng hàm getset

var circle = {
  radius : 1,    // Bán kính
  get diameter()      { return this.radius * 2; }, // Gọi ra đường kính từ bán kính
  set diameter(value) { this.radius = value / 2; } // Trả ra bán kính từ đường kính
};

circle.diameter = 5;          // Gọi set diameter
console.log(circle.radius);   // => 2.5
console.log(circle.diameter); // => 5 (Gọi get diameter)

5. Định nghĩa class

Có thể định nghĩa class của Javascript bằng bằng định nghĩa property trong constructor

// Đinh nghĩa Person class (constructor)
var Person = function(name, age) {
  this.name = name;
  this.age  = age;
}

// Tạo Person instance
var satoshi = new Person('Satoshi', 28);
console.log(satoshi.name); // => 'Satoshi'
console.log(satoshi.age);  // => 28
console.log(satoshi instanceof Person) // => true (satoshi là instance của class Person)

6. Constructor argument

Với Javascript có thể lược bỏ đối số khi gọi hàm. Vì constructor cũng là hàm nên có thể lược bỏ đối số của constructor. Nhưng khi đó, constructor có đối số bị lược bỏ được gọi ra để tạo instance thì sẽ có lỗi property chưa được định nghĩa (undefined).

Có 2 cách giải quyết:

  • Khởi tạo giá trị xác định cho property

  • Ngắt chương trình tạo ra lỗi

Khởi tạo giá trị xác định cho property

var Person = function(name, age) {
  // Khởi tạo giá trị xác định
  this.name = name || 'No name';
  this.age  = age  || 20;
}

// Khi không xác định đối số cho constructor, giá trị khởi tạo được sử dụng
var satoshi = new Person();
console.log(satoshi.name); // => 'No name'
console.log(satoshi.age);  // => 20

// Khi xác định đối số cho constructor, giá tị này được sử dụng
var satoshi = new Person('サトシ', 28);
console.log(satoshi.name); // => 'サトシ'
console.log(satoshi.age);  // => 28

Ngắt chương trình tạo ra lỗi

var Person = function(name, age) {
  // Kiểm tra đối số
  if (name == undefined) { throw new Error("Please define argument 'name'"); }
  if (age == undefined)  { throw new Error("Please define argument 'age'"); }

  this.name = name;
  this.age  = age;
}

// Khi không xác định đối số của constructor, lỗi sẽ xuất hiện
var satoshi = new Person(); // => Error: Please define argument 'name'

7. Định nghĩa instance method

Có 2 cách định nghĩa method cho object trong Javascript:

  • Gán function cho property
  • Sử dụng object đặc chủng prototype

Gán function cho property

var Person = function(name, age) {
  this.name = name;
  this.age  = age;

  // Gán function cho property
  this.greet = function() {
    console.log('Hello, ' + this.name);
  }
}

var satoshi = new Person('サトシ', 20);

// Có thể gọi method ra
satoshi.greet(); // => 'Hello, サトシ'

Vấn đề của phương pháp này là khi tạo instance bằng cách new Person(...), method greet() cũng được tạo ra nên khi tạo instance, sẽ lãng phí bộ nhớ. Để khắc phục điều này, có thể dùng object đặc chủng là prototype như dưới đây.

Sử dụng object đặc chủng prototype

var Person = function(name, age) {
  this.name = name;
  this.age  = age;
}

// Gán method cho tất cả các object property của class
Person.prototype.greet = function() {
  console.log('Hello, ' + this.name);
}

var satoshi = new Person('サトシ', 20);

// Có thể gọi method ra
satoshi.greet(); // => 'Hello, サトシ'

Nếu làm thế thì tất cả các instance Person khi tham chiếu đến Person.prototype.greet() nên sẽ không lãng phí bộ nhớ.

Thứ tự xử lý tbên trong Javascript là khi greet() method được gọi ra, đầu tiên sẽ tìm kiếm property được định nghĩa trong Person instance, nếu không tìm thấy sẽ tìm kiếm trong Person.prototype, khi tìm thấy sẽ chạy method greet()

8. Định nghĩa class property và class method

Class method của Javascript có thể được định nghĩa tương tự như định nghĩa class property.

var Person = function(name, age) {
  this.name = name;
  this.age  = age;
}

// Định nghĩa class property (hằng số)
// Biến lưu giá trị dưới bao nhiêu tuổi thì được gọi là trẻ young
Person.YOUNG_LIMIT_AGE = 20;

// Đinh nghĩa class method
// Nếu là young thì trả về true, ngược lại thì trả về false
Person.isYoung = function(age) {
  if (age <= Person.YOUNG_LIMIT_AGE) {
    return true;
  }
  return false;
}

// Gọi ra class propety
console.log(Person.YOUNG_LIMIT_AGE); // => 20

// Gọi ra class method
console.log(Person.isYoung(10)); // => true
console.log(Person.isYoung(30)); // => false

Hằng số thường được định nghĩa bởi chữ viết hoa nhưng không có quy định không thể thay đổi như trong Ruby.

9. Kế thừa class

Trong Javascript không có cấu trúc để kế thừa, thực hiện kế thừa bằng cách sau:

  • Kế thừa property: goi method apply
  • Kế thừa method: sử dụng object prototype

Việc kế thừa class trong Javascript rất phức tạp, với code nhỏ thì không dùng, với code lớn thì dùng các library để kế thừa.

Kế thừa property: goi method apply Trong constructor của sub class, nếu gọi method apply của super class, sub class có thể kế thừa property của super class.

// Person class
var Person = function(name, ageavascript) {
  this.name = name;
  this.age  = age;
}

// Employee kế thừa Person
var Employee = function(name, age, jobTitle) {
  this.jobTitle = jobTitle;

  // Gọi constructor của super class
  Person.apply(this, [name, age]);
}

var satoshi = new Employee('サトシ', 28, 'QA');
console.log(satoshi.name);     // => 'サトシ'
console.log(satoshi.age);      // => 28
console.log(satoshi.jobTitle); // => 'QA'
console.log(satoshi instanceof Employee); // => true

Kế thừa method: sử dụng object prototype

Có thể sử dụng prototype chain của object prototype để kế thừa

// Định nghĩa Person class
var Person = function(name, age) {
  this.name = name;
  this.age  = age;
}

// Định nghĩa method Person#greet()
Person.prototype.greet = function() {
  console.log('Hello, ' + this.name);
}

// Đinh nghĩa class Employee kế thừa Person
var Employee = function(name, age, jobTitle) {
  this.jobTitle = jobTitle;

  // Kế thừa property của Person
  Person.apply(this, [name, age]);
}

// Set Person instance bằng prototype chain
// Khi gọi method Employee instance, tìm kiếm xem method có trong trong method Person.prototype không
Employee.prototype = Object.create(Person.prototype);

// Ở phía trên constructor property trở thành Person nên ở đây set lại là Employee
Employee.prototype.constructor = Employee;

var satoshi = new Employee('サトシ', 28, 'QA');
satoshi.greet();                  // => 'Hello, サトシ'
console.log(satoshi.constructor); // => Employee constructor ( function(name, age, jobTitle) { ... } )

10. Kế thừa class property và class method

Về cơ bản, trong Javascript không thực hiện kế thừa class property và class method. Ở đây, Sub class sử dụng class property và class method của super class nên nếu kế thừa, các định nghĩa property cần copy xuống sub class nên sẽ bị lặp code.