Đối tượng (object)
Các kiểu đơn giản nhất trong JavaScript là kiểu số (numbers
), kiểu chuỗi (strings
), kiểu luận lý (booleans
: gồm hai giá trị true
, false
), null
và undefined
. Chúng có thể là các object nhưng immutable (không thay đổi khi được tạo ra). Ngoài những kiểu đơn giản đó, mọi thứ trong JavaScript đều là object và có thể thay đổi (mutable): mảng, hàm, biểu thức thường quy (regular expression),…
Object có ích trong việc lưu trữ và tổ chức dữ liệu, đồng thời mỗi object có thể chứa bên trong nó các object khác nên nó có thể được tổ chức theo cây hay đồ thị. Các object có thể được thừa kế.
Đối tượng (object) và các thuộc tính (properties)
Object chứa các thuộc tính (properties) – đó là cặp tên (name) và giá trị (value). Tên thuộc tính là một chuỗi (có thể là rỗng), giá trị thuộc tính có thể là bất kì giá trị nào trong JavaScript trừ undefined
. Đối tượng có thể không chứa hay chứa nhiều thuộc tính.
Ví dụ: Tạo đối tượng không có thuộc tính:
let empty_person = { };
Tạo đối tượng chứa các thuộc tính và giá trị:
let person = {firstname:"John", lastname:"Doe", age:50};
Chúng ta sẽ có các tên thuộc tính là firstname, lastname và age. Giá trị là John, Doe và 50 tương ứng các thuộc tính trên.
Cách thực hành phổ biến khi khai báo một đối tượng là dùng từ khóa const:
const person = {firstname:"John", lastname:"Doe", age:50};
Các thao tác với thuộc tính đối tượng
Các thuộc tính thường có thể được thay đổi, thêm và xóa, nhưng một số thuộc tính chỉ được đọc.
Truy cập thuộc tính
Thuộc tính có thể được truy cập theo nhiều cách, cách phổ biến là sử dụng dấu chấm sau tên đối tượng:
tên_đối_tượng.tên_thuộc_tính
Ví dụ:
person["firstname"] + " is " + person["age"] + " years old.";
Duyệt qua các thuộc tính
JS sử dụng vòng lặp for..in để duyệt qua các thuộc tính của một đối tượng, cú pháp:
for (let bien in doi_tuong) {
// mã thực thi
}
Ví dụ:
const person = {
fname:" John",
lname:" Doe",
age: 25
};
for (let x in person) {
txt += person[x];
}
Thêm thuộc tính mới đến đối tượng
Chúng ta có thể thêm một hay nhiều thuộc tính mới đến một đối tượng đã tồn tại trước đó chỉ đơn giản là gán giá trị đến các thuộc tính mới này. Ví dụ chúng ta thêm thuộc tính nationality đến đối tượng person:
person.nationality = "English";
Xóa thuộc tính của một đối tượng
Để xóa thuộc tính của một đối tượng chúng ta dùng từ khóa delete. Ví dụ xóa thuộc tính age của đối tượng person:
delete person.age;
hoặc
delete person["age"];
Từ khóa delete xóa cả giá trị của thuộc tính và chính thuộc tính đó. Sau khi xóa, thuộc tính không thể được sử dụng trước khi được thêm lại. Toán tử delete được thiết kế để sử dụng trên các thuộc tính đối tượng. Nó không ảnh hưởng đến các biến hoặc hàm. Toán tử delete không được sử dụng trên các thuộc tính đối tượng JavaScript được xác định trước. Nó có thể làm hỏng ứng dụng của bạn.
Các đối tượng lồng nhau (nested objects)
Giá trị trong một đối tượng có thể là một đối tượng khác. Ví dụ thuộc tính cars của đối tượng myObj là một đối tượng:
myObj = {
name:"John",
age:30,
cars: {
car1:"Ford",
car2:"BMW",
car3:"Fiat"
}
}
Để truy cập các đối tượng này (cars) chúng ta dùng dấu chấm:
myObj.cars.car2;
hay dùng ngoặc vuông:
myObj.cars["car2"];
hoặc
myObj["cars"]["car2"];
hoặc
let p1 = "cars";
let p2 = "car2";
myObj[p1][p2];
Mảng và đối tượng lồng nhau Giá trị các thuộc tính của đối tượng có thể là các mảng, và các giá trị trong mảng có thể là các đối tượng. Ví dụ:
const myObj = {
name: "John",
age: 30,
cars: [
{name:"Ford", models:["Fiesta", "Focus", "Mustang"]},
{name:"BMW", models:["320", "X3", "X5"]},
{name:"Fiat", models:["500", "Panda"]}
]
}
Để truy cập các mảng trong mảng, dùng vòng lặp for – in:
for (let i in myObj.cars) {
x += "<h1>" + myObj.cars[i].name + "</h1>";
for (let j in myObj.cars[i].models) {
x += myObj.cars[i].models[j];
}
}
Đối tượng (object) và các phương thức (methods)
Các phương thức (methods) JavaScript là các hành động có thể được thực hiện trên các đối tượng. Phương thức JavaScript là một thuộc tính chứa định nghĩa hàm. Ví dụ thuộc tính fullName của đối tượng person:
const person = {
firstName: "John",
lastName: "Doe",
id: 5566,
fullName: function() {
return this.firstName + " " + this.lastName;
}
};
Trong JavaScript, từ khóa this tham chiếu đến một đối tượng, đối tượng nào phụ thuộc vào cách this đang được gọi (sử dụng hoặc gọi). Từ khóa this tham chiếu đến các đối tượng khác nhau tùy thuộc vào cách nó được sử dụng:
- Trong một phương thức đối tượng, this tham chiếu đến đối tượng.
- Một mình, this tham chiếu đến đối tượng toàn cục (global object).
- Trong một hàm, điều này đề cập đến đối tượng toàn cục.
- Trong một hàm, ở chế độ nghiêm ngặt (strict mode), this là undefined.
- Trong một sự kiện, this tham chiếu đến phần tử đã nhận sự kiện.
- Các phương thức như call (), apply () và bind () có thể tham chiếu đến bất kỳ đối tượng nào.
Truy cập phương thức
Truy cập phương thức của một đối tượng dùng dấu chấm theo cú pháp:
tên_đối_tượng.tên_phương_thức()
Chú ý sau tên phương thức (tên_phương_thức) có cặp ngoặc tròn. Ví dụ:
name = person.fullName();
Nếu chỉ dùng tên phương thức mà không dùng cặp ngoặc tròn như sau:
name = person.fullName;
thì kết quả trả về là định nghĩa hàm.
Thêm một phương thức mới
Thêm một phương thức mới tương tự thêm một thuộc tính mới đến một đối tượng. Chúng ta cũng có thể kết hợp các phương thức được xây dựng sẵn (build-in methods) đến các phương thức của đối tượng. Ví dụ sử dụng phương thức toUpperCase() kết hợp với phương thức mới (tạm gọi là upperCaseName) được thêm vào đối tượng person:
person.upperCaseName = function () {
return (this.firstName + " " + this.lastName).toUpperCase();
};
Getter và Setter (gọi chung là Accessors)
Xét đối tượng person được định nghĩa như sau:
const person = {
firstName: "John",
lastName: "Doe",
language: "",
};
Thuộc tính của các đối tượng là các dữ liệu quan trọng mà chúng ta không muốn người dùng có thể truy cập trực tiếp. Đây là tính đóng gói (encapsulation) – một trong những trụ cột quan trọng của mô hình lập trình hướng đối tượng.
Truy cập của người dùng đến thuộc tính đối tượng bao gồm hai hành động chính:
- Sử dụng giá trị các thuộc tính đó hay còn được gọi là hành động get. Ví dụ:
console.log(person.language);
- Thiết lập hay thay đổi (can thiệp) giá trị thuộc tính đó hay còn được gọi là hành động set. Ví dụ:
person.language = "en";
Getter và setter là cách định nghĩa cho phép người dùng truy cập (thông qua các hành động get hay set) một cách gián tiếp đến các thuộc tính để mang lại sự hiệu quà và an toàn (bảo mật) hơn. JavaScript cung cấp từ khóa set cho setter và get cho getter.
Cú pháp một setter:
set tên_setter (value){
this.tên_thuộc_tính = value;
}
Xét ví dụ sau:
const person = {
firstName: "John",
lastName: "Doe",
language: "",
set lang(value) {
this.language = value;
}
};
Chúng ta sử dụng từ khóa set để định nghĩa setter cho thuộc tính language gọi là lang. Bây giờ, để gán giá trị cho thuộc tính language, chúng ta sẽ thông qua lang:
person.lang = "en";
Cú pháp một getter:
get tên_getter (){
return this.tên_thuộc_tính;
}
Chúng ta cũng có thể định nghĩa getter cho thuộc tính language như sau:
const person = {
firstName: "John",
lastName: "Doe",
language: "en",
get lang() {
return this.language.toUpperCase();
}
};
Sử dụng thuộc tính language thông qua lang.
Console.log(person.lang); // EN
Phương thức khởi tạo đối tượng (Object Constructors)
Giả sử chúng ta có đối tượng person1 như sau:
const person1 = {
firstName: "John",
lastName: "Doe",
age: 50
};
Và đối tượng person2 như sau:
const person2 = {
firstName: "Sally",
lastName: "Rally",
age: 48
};
Để ý rằng, hai đối tượng person1 và person2 có cùng các thuộc tính là firstName, lastName và age. Hai đối tượng như thế này được gọi là cùng kiểu.
Đối với các đối tượng cùng kiểu, chúng ta có thể định nghĩa các đối tượng này hiệu quả hơn bằng cách dùng hàm khởi tạo (object constructor).
Nếu đối tượng là một cái gì hay ai cụ thể như person1 hay person2 thì hàm khởi tạo giống như một bản thiết kế tổng quát (blueprint) cho các đối tượng này. (Trong ES6 sử dụng class nhưng sẽ được bàn trong phần kế tiếp.)
Ví dụ hàm khởi tạo Person cho hai đối tượng person1 và person2 như sau:
function Person(first, last, age) {
this.firstName = first;
this.lastName = last;
this.age = age;
}
Chú ý rằng:
- Hàm khởi tạo bắt đầu bằng chữ cái in hoa;
- Trong một hàm khởi tạo, this không có giá trị. Nó là một thay thế cho đối tượng mới. Giá trị của this sẽ trở thành đối tượng mới khi một đối tượng mới được tạo.
Các đối tượng mới được tạo từ hàm khởi tạo bằng cách sử dụng từ khóa new.
const person1 = new Person("John", "Doe", 50);
const person2 = new Person("Sally", "Rally", 48);
Chúng ta có thể thêm các thuộc tính hay phương thức mới đến tất cả các đối tượng cùng kiểu (như person1 và person2) chỉ đơn giản là thêm chúng đến hàm khởi tạo (Person). Tuy nhiên, chúng ta không được phép thêm các thuộc tính hay phương thức mới theo kiểu các đối tượng. Ví dụ thêm thuộc tính eyeColor đến hàm khởi tạo Person như sau là không hợp lệ:
Person.eyeColor = "Blue";
Cách chính xác phải là:
function Person(first, last, age) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = "Blue";
}
Nếu một đối tượng nào đó, ví dụ person1, muốn thêm một thuộc tính hay phương thức mới, chúng ta sẽ thêm trực tiếp thuộc tính hay phương thức đến đối tượng như đã biết.
Prototype
Trong JS, mọi đối tượng sẽ thừa kế các thuộc tính và phương thức từ một prototype (nguyên mẫu). Ví dụ đối tượng Person sẽ thừa kế từ Person.prototype, đối tượng Date thừa kế từ Date.prototype.
Prototype object chuẩn trong JavaScript mà mọi đối tượng đều liên kết đến là Object.prototype.
Như đã đề cập trong bài hàm khởi tạo, chúng ta không được phép thêm thuộc tính hay phương thức mới đến hàm khởi tạo như ví dụ thêm thuộc tính eyeColor như sau:
Person.eyeColor = "Blue";
Cách chính xác phải là:
function Person(first, last, age) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = "Blue";
}
Với prototype, chúng ta có thể thêm thuộc tính và phương thức mới đơn giản hơn theo cú pháp:
Tên_hàm_khởi_tạo.prototype.tên_thuộc_tính = giá_trị
Hay
Tên_hàm_khởi_tạo.prototype.tên_hàm = function() {...}
Ví dụ thêm thuộc tính eyeColor đến Person:
Person.prototype.eyeColor = "Blue";
Thêm hàm name:
Person.prototype.name = function() {
return this.firstName + " " + this.lastName;
};