Như chúng ta đã biết từ chương Kiểu dữ liệu, có tám loại dữ liệu trong JavaScript. Bảy cái trong số họ được gọi là kiểu nguyên thủy, vì giá trị của nó chỉ chứa một thứ duy nhất (có thể là một chuỗi hoặc một số hoặc bất cứ thứ gì).

Ngược lại, các đối tượng(Object) được sử dụng để lưu trữ các bộ sưu tập có kiểu các dữ liệu khác nhau và các thực thể phức tạp hơn. Trong JavaScript, các đối tượng thâm nhập vào hầu hết mọi khía cạnh của ngôn ngữ. Vì vậy, chúng ta phải hiểu chúng trước khi đi sâu vào bất cứ nơi nào khác.

Một đối tượng(Object) có thể được tạo bằng dấu ngoặc {…}với một danh sách các thuộc tính tùy chọn. Một thuộc tính là một khóa chính: giá trị cặp, trong đó keylà một chuỗi (còn được gọi là tên thuộc tính) và valuecó thể là bất cứ thứ gì.

Chúng ta có thể tưởng tượng một đối tượng như một cái tủ với các tập tin. Mỗi phần dữ liệu được lưu trữ trong tệp của nó bằng một từ khóa. Thật dễ dàng để tìm một tệp theo tên của nó hoặc thêm / xóa một tệp.

Có thể tạo một đối tượng trống (tủ trống) bằng một trong hai cú pháp:

let user = new Object(); // "object constructor" syntax
let user = {};  // "object literal" syntax

Thông thường, các dấu ngoặc nhọn {...}được sử dụng. Khai báo đó được gọi là một đối tượng theo nghĩa đen .

1. Chữ và thuộc tính

Chúng ta có thể ngay lập tức đặt một số thuộc tính vào {...}dưới dạng cặp khóa: giá trị:

let user = {     // an object
  name: "John",  // by key "name" store value "John"
  age: 30        // by key "age" store value 30
};

Một thuộc tính có một từ khóa (còn được gọi là tên của nó hay tên nhận dạng của nó) trước dấu hai chấm ":"và giá trị ở bên phải của nó.

Trong đối tượnguser, có hai thuộc tính:

  1. Thuộc tính đầu tiên có tên "name"và giá trị "David".
  2. Cái thứ hai có tên "age"và giá trị 30.

Đối tượng user có thể được tưởng tượng như một cái tủ với hai tệp có tên là age và name.

Chúng ta có thể thêm, xóa và đọc các tập tin từ nó bất cứ lúc nào.

Có thể truy cập các giá trị thuộc tính bằng cách sử dụng ký hiệu chấm:

// get property values of the object:
alert( user.name ); // John
alert( user.age ); // 30

Giá trị có thể là bất kỳ kiểu nào. Hãy thêm một boolean:

user.isAdmin = true;

Để xóa một thuộc tính, chúng ta có thể sử dụng toán tửdelete:

delete user.age;

Chúng ta cũng có thể sử dụng tên thuộc tính đa từ, nhưng sau đó chúng phải được trích dẫn:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

let user = {
  name: "John",
  age: 30,
  "likes birds": true  // multiword property name must be quoted
};

Thuộc tính cuối cùng trong danh sách có thể kết thúc bằng dấu phẩy:

 let user = {
  name: "John",
  age: 30,
}

Thêm dấu phẩy, làm nó dễ dàng tách biệt hơn các thuộc tính để thêm / xóa / di chuyển xung quanh các thuộc tính khác, bởi vì tất cả các dòng trở nên giống nhau.

Đối tượng với const có thể được thay đổi

Xin lưu ý: một đối tượng được khai báo là const có thể được sửa đổi.

Ví dụ:

const user = {
  name: "John"
};

user.name = "Pete"; // (*)

alert(user.name); // Pete

Có vẻ như dòng (*)này sẽ gây ra lỗi, nhưng không. Các constsửa chữa giá trị của một thuộc tính tronguser, nhưng không phải nội dung của nó.

Điều constnày sẽ chỉ đưa ra một lỗi nếu chúng ta cố gắng thiết lập toàn bộuser=....

Có một cách khác để tạo các thuộc tính đối tượng không đổi, chúng ta sẽ trình bày nó sau trong chương Các cờ và mô tả thuộc tính.

2. Dấu ngoặc vuông

Đối với các thuộc tính đa từ, truy cập bằng dấu chấm sẽ không hoạt động:

// this would give a syntax error
user.likes birds = true

JavaScript không hiểu điều đó. Nó nghĩ rằng chúng ta giải quyết user.likes, và sau đó đưa ra một lỗi cú pháp khi gặp sự cố không mong muốn với birds.

Dấu chấm với từ khóa là một định danh biến hợp lệ. Điều đó ngụ ý: không chứa khoảng trắng, không bắt đầu bằng một chữ số và không bao gồm các ký tự đặc biệt ( $_được phép).

Có một ký hiệu khung hình vuông để thay thế việc gán, lấy giá trị:

let user = {};

// set
user["likes birds"] = true;

// get
alert(user["likes birds"]); // true

// delete
delete user["likes birds"];

Bây giờ mọi thứ đều ổn. Xin lưu ý rằng chuỗi bên trong ngoặc được trích dẫn chính xác với từ khoá trong object khi muốn lấy giá trị ra.

Dấu ngoặc vuông cũng cung cấp một cách để có được tên thuộc tính là bằng một biến nào đó:

let key = "likes birds";

// same as user["likes birds"] = true;
user[key] = true;

Ở đây, biến keycó thể được tính vào thời gian chạy hoặc phụ thuộc vào đầu vào của người dùng. Và sau đó chúng ta sử dụng nó để truy cập vào thuộc tính. Điều đó cho chúng ta rất nhiều sự linh hoạt.

Ví dụ:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

let user = {
  name: "David",
  age: 30
};

let key = prompt("What do you want to know about the user?", "name");

// access by variable
alert( user[key] ); // John (if enter "name")

Ký hiệu dấu chấm không thể được sử dụng theo cách tương tự:

let user = {
  name: "David",
  age: 30
};

let key = "name";
alert( user.key ) // undefined

3. Thuộc tính tính toán

Chúng ta có thể sử dụng dấu ngoặc vuông trong một đối tượng khi tạo một đối tượng. Đó gọi là thuộc tính tính toán .

Ví dụ:

let fruit = prompt("Which fruit to buy?", "apple");

let bag = {
  [fruit]: 5, // the name of the property is taken from the variable fruit
};

alert( bag.apple ); // 5 if fruit="apple"

Ý nghĩa của một thuộc tính được tính toán rất đơn giản: [fruit]có nghĩa là tên thuộc tính nên ta có thể lấy nó để lấy dữ liệu ra fruit.

Vì vậy, nếu một khách truy cập vào "apple", bagsẽ trở thành là {apple: 5}. Biến fruit sẽ được thay thế bằng apple.

Về cơ bản, nó hoạt động giống như:

let fruit = prompt("Which fruit to buy?", "apple");
let bag = {};

// take property name from the fruit variable
bag[fruit] = 5;

Nhưng nhìn đẹp hơn.

Chúng ta có thể sử dụng các biểu thức phức tạp hơn trong dấu ngoặc vuông:

let fruit = 'apple';
let bag = {
  [fruit + 'Computers']: 5 // bag.appleComputers = 5
};

Dấu ngoặc vuông mạnh hơn nhiều so với ký hiệu dấu chấm. Họ cho phép truy cập giá trị với tên thuộc tính bất kỳ và các biến. Nhưng nó sẽ cồng kềnh hơn để viết.

Vì vậy, hầu hết thời gian, khi tên thuộc tính được viết khá gọn và đơn giản, dấu chấm được sử dụng. Và nếu chúng ta cần một cái gì đó phức tạp hơn, thì chúng ta chuyển sang dấu ngoặc vuông.

4. Thuộc tính gắn gọn

Trong code thực tế hiện nay, chúng ta thường sử dụng các biến, hiện đang làm giá trị cho tên thuộc tính.

Ví dụ:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

function makeUser(name, age) {
  return {
    name: name,
    age: age,
    // ...other properties
  };
}

let user = makeUser("David", 30);
alert(user.name); // David

Trong ví dụ trên, các thuộc tính có cùng tên với các biến. Trường hợp sử dụng để tạo một thuộc tính từ một biến là rất phổ biến, có một cách làm cho giá trị thuộc tính đặc biệt để làm cho nó ngắn hơn. Cách đó được gọi là shorthand

Thay vì name:namechúng ta chỉ có thể viết name, như thế này:

function makeUser(name, age) {
  return {
    name, // same as name: name
    age,  // same as age: age
    // ...
  };
}

Chúng ta có thể sử dụng cả thuộc tính bình thường và cách viết gọn trong cùng một đối tượng:

let user = {
  name,  // same as name:name
  age: 30
};

5. Giới hạn tên thuộc tính

Như chúng ta đã biết, một biến không thể có một tên bằng một trong những từ dành riêng cho ngôn ngữ như là “for”, “let”, “return” etc..

Nhưng đối với một thuộc tính của đối tượng, không có hạn chế nào như vậy:

// these properties are all right
let obj = {
  for: 1,
  let: 2,
  return: 3
};

alert( obj.for + obj.let + obj.return );  // 6

Nói tóm lại, không có giới hạn về tên thuộc tính. Chúng có thể là bất kỳ chuỗi hoặc ký hiệu nào (một kiểu đặc biệt cho các định danh sẽ được đề cập sau).

Các loại khác được tự động chuyển đổi thành chuỗi.

Chẳng hạn, một số 0trở thành một chuỗi "0"khi được sử dụng làm từ khóa thuộc tính:

let obj = {
  0: "test" // same as "0": "test"
};

// both alerts access the same property (the number 0 is converted to string "0")
alert( obj["0"] ); // test
alert( obj[0] ); // test (same property)

Có một thuộc tính đặc biệt có tên là __proto__. Chúng ta không thể gán cho nó thành một giá trị phi đối tượng(các kiểu nguyên thuỷ):

let obj = {};
obj.__proto__ = 5; // assign a number
alert(obj.__proto__); // [object Object] - the value is an object, didn't work as intended

Như chúng ta thấy từ code, việc gán cho một giá trị nguyên thủy là 5 sẽ bị bỏ qua.

Chúng ta sẽ giới thiệu các tính chất đặc biệt của __proto__trong chương tiếp theo , và một số gợi ý các cách để giải quyết hành vi như vậy.

6. Kiểm tra sự tồn tại của thuộc tính, toán tử ‘in’

Một tính năng đáng chú ý của các đối tượng trong JavaScript, so với nhiều ngôn ngữ khác, là có thể truy cập bất kỳ thuộc tính nào. Sẽ không có lỗi nếu thuộc tính đó tồn tại!

Đọc một thuộc tính không tồn tại chỉ cần trả lại undefined. Vì vậy, chúng tôi có thể dễ dàng kiểm tra xem tài sản có tồn tại hay không:

let user = {};

alert( user.noSuchProperty === undefined ); // true means "no such property"

Ngoài ra còn có một toán tử đặc biệt là "in" để làm điều đó.

Cú pháp là:

"key" in object

Ví dụ:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

let user = { name: "John", age: 30 };

alert( "age" in user ); // true, user.age exists
alert( "blabla" in user ); // false, user.blabla doesn't exist

Xin lưu ý rằng ở phía bên trái của inphải có một tên thuộc tính. Đó thường là một chuỗi trích dẫn.

Nếu chúng ta bỏ qua dấu ngoặc kép, điều đó có nghĩa là một biến, nó sẽ chứa tên thực tế cần kiểm tra. Ví dụ:

let user = { age: 30 };

let key = "age";
alert( key in user ); // true, property "age" exists

Tại sao toán tử in tồn tại? Nhưng tại sao không so sánh với undefined?

Vâng, hầu hết thời gian so sánh với undefined sẽ hoạt động tốt. Nhưng có một trường hợp đặc biệt khi nó thất bại, nhưng "in"hoạt động chính xác.

Đó là khi một thuộc tính của đối tượng tồn tại, nhưng lưu trữ undefined:

let obj = {
  test: undefined
};

alert( obj.test ); // it's undefined, so - no such property?

alert( "test" in obj ); // true, the property does exist!

Trong code trên, thuộc tính obj.test tồn tại. Vì vậy, các toán tử in làm việc ngay.

Các tình huống như thế này rất hiếm khi xảy ra, vì undefinedkhông được chỉ định rõ ràng. Chúng ta chủ yếu sử dụng nullcho các giá trị không rõ ràng hoặc Vì vậy, các toán tửin được sử dụng trong code.

7. Vòng lặp For .. In

Để đi qua tất cả các từ khóa thuộc tính của một đối tượng, thì chúng ta có vòng lặp : for..in. Đây là một điều hoàn toàn khác với cấu trúc for(;;) mà chúng ta đã nghiên cứu trước đây.

Cú pháp:

for (key in object) {
  // executes the body for each key among object properties
}

Ví dụ: hãy xuất tất cả các thuộc tính của user:

let user = {
  name: "David",
  age: 30,
  isAdmin: true
};

for (let key in user) {
  // keys
  alert( key );  // name, age, isAdmin
  // values for the keys
  alert( user[key] ); // John, 30, true
}

Lưu ý rằng tất cả các cấu trúc của người dùng cho các ứng dụng trên mạng cho phép chúng ta khai báo biến vòng lặp bên trong vòng lặp, như let keyở đây.

Ngoài ra, chúng ta có thể sử dụng một tên biến khác ở đây thay vì key. Ví dụ, "for (let prop in obj)"cũng được sử dụng rộng rãi.

8. Thứ tự thuộc tính của một đối tượng

Nếu chúng ta lặp qua một đối tượng, chúng ta có nhận được tất cả các thuộc tính theo cùng thứ tự chúng đã được thêm không?

Câu trả lời ngắn gọn là: Chúng được sắp xếp theo thứ tự đặc biệt: các thuộc tính số nguyên được sắp xếp theo thứ tự tăng dần, các thuộc tính khác được sắp xếp theo thứ tự được tạo. Các chi tiết theo sau.

Ví dụ: hãy xem xét một đối tượng code điện thoại sau:

let codes = {
  "84": "Việt Nam",
  "41": "Switzerland",
  "44": "Great Britain",
  // ..,
  "1": "USA"
};

for (let code in codes) {
  alert(code); // 1, 41, 44, 49
}

Đối tượng có thể được sử dụng để đề xuất một danh sách các tùy chọn cho người dùng. Nếu chúng ta tạo một trang chủ yếu cho khán giả Việt Nam thì có lẽ chúng ta muốn 84 thuộc tính đầu tiên.

Nhưng nếu chúng ta chạy code, chúng ta sẽ thấy một bức tranh hoàn toàn khác:

  • USA (1) đi trước
  • Rồi Switzerland (41) và cứ thế.

Các code đi theo thứ tự tăng dần, bởi vì chúng là số nguyên. Vì vậy, chúng tôi thấy 1, 41, 44, 84.

Thuộc tính số nguyên?

Thuộc tính số nguyên của số hạng ở đây có nghĩa là một chuỗi có thể được chuyển đổi số và ngược lại.

Vì vậy, 49 là một tên thuộc tính số nguyên, bởi vì khi nó được chuyển thành số nguyên và trở lại, nó vẫn giống nhau. Không có gì khác nhau:

// Math.trunc is a built-in function that removes the decimal part
alert( String(Math.trunc(Number("49"))) ); // "49", same, integer property
alert( String(Math.trunc(Number("+49"))) ); // "49", not same "+49" ⇒ not integer property
alert( String(Math.trunc(Number("1.2"))) ); // "1", not same "1.2" ⇒ not integer property

Mặt khác, nếu các khóa không phải là số nguyên, thì chúng được liệt kê theo thứ tự tạo, ví dụ:

let user = {
  name: "John",
  surname: "Smith"
};
user.age = 25; // add one more

// non-integer properties are listed in the creation order
for (let prop in user) {
  alert( prop ); // name, surname, age
}

Vì vậy, để khắc phục sự cố với mã điện thoại, chúng ta có thể gian lận bằng cách làm cho code không phải là số nguyên. Thêm một "+"dấu cộng trước mỗi code là đủ.

Như thế này:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

let codes = {
  "+49": "Germany",
  "+41": "Switzerland",
  "+44": "Great Britain",
  // ..,
  "+1": "USA"
};

for (let code in codes) {
  alert( +code ); // 49, 41, 44, 1
}

Bây giờ nó hoạt động như dự định.

9. Tóm lược

Đối tượng là một mảng các thuộc tính kết hợp với một số tính năng đặc biệt.

Họ lưu trữ các thuộc tính (cặp khóa-giá trị = key – value), trong đó:

  • Khóa thuộc tính phải là chuỗi hoặc ký hiệu (thường là chuỗi).
  • Các giá trị có thể là bất kỳ kiểu nào.

Để truy cập một thuộc tính, chúng ta có thể sử dụng:

  • Ký hiệu dấu chấm : obj.property.
  • Ký hiệu ngoặc vuông obj["property"]. Dấu ngoặc vuông cho phép lấy khóa từ một biến, như obj[varWithKey].

Một số Toán tử:

  • Để xóa một thuộc tính : delete obj.prop.
  • Để kiểm tra xem một thuộc tính có từ khóa nào đó đã có tồn tại không : "key" in obj.
  • Để lặp qua một đối tượng: for (let key in obj)loop.

Những gì chúng ta đã nghiên cứu trong chương này được gọi là một đối tượng đơn giản, hay chỉ là Object.

Có nhiều loại đối tượng khác trong JavaScript:

  • Array để lưu trữ các bộ sưu tập dữ liệu theo thứ tự,
  • Date để lưu trữ thông tin về ngày và thời gian,
  • Error để lưu trữ thông tin về một lỗi.
  • …Và nhiều thứ khác.

Object có những tính năng đặc biệt mà chúng ta sẽ nghiên cứu sau. Đôi khi, mọi người nói một cái gì đó giống như kiểu Array, hay kiểu Date, nhưng chính thức thì chúng không phải là kiểu của riêng của chúng ta tạo, mà thuộc về một kiểu dữ liệu đối tượng duy nhất của Javascript định nghĩa sẵn để dùng chung. Và chúng ta có thể mở rộng nó theo nhiều cách khác nhau.

Các đối tượng trong JavaScript rất mạnh mẽ. Ở đây chúng ta vừa luớt qua một chủ đề thực sự rất lớn. Chúng ta sẽ làm việc chặt chẽ với các đối tượng và tìm hiểu thêm về chúng trong các phần tiếp theo của hướng dẫn này.

Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha.

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!