Thuộc tính của đối tượng Object trong JavaScript

Javascript nâng cao | by Học Javascript

Trong JavaScript, Object là một trong những kiểu dữ liệu quan trọng nhất, đóng vai trò là nền tảng cho hầu hết các cấu trúc dữ liệu phức tạp. Mỗi Object bao gồm các thuộc tính (properties), là tập hợp các cặp key-value, giúp lưu trữ và quản lý dữ liệu một cách linh hoạt.

Hiểu rõ về các thuộc tính của Object không chỉ giúp lập trình viên thao tác hiệu quả hơn mà còn tối ưu hóa hiệu suất, quản lý trạng thái dữ liệu chặt chẽ và tránh các lỗi không mong muốn. Trong bài viết này, chúng ta sẽ cùng tìm hiểu các loại thuộc tính trong Object, cách truy cập, chỉnh sửa, kiểm soát và bảo vệ chúng trong JavaScript.

Thuộc tính (Property) là gì?

Trong JavaScript, thuộc tính (property) là một cặp key-value được lưu trữ bên trong một đối tượng (Object). Thuộc tính giúp Object có thể chứa dữ liệu và biểu diễn thông tin.

Ví dụ về một Object chứa các thuộc tính:

const person = {
  name: "John",  // Thuộc tính name với giá trị "John"
  age: 30,       // Thuộc tính age với giá trị 30
  city: "New York" // Thuộc tính city với giá trị "New York"
};

Cấu trúc của một thuộc tính trong Object

Mỗi thuộc tính trong Object bao gồm hai phần:

  • Key (Tên thuộc tính): Luôn là một chuỗi (string) hoặc một Symbol.

  • Value (Giá trị thuộc tính): Có thể là bất kỳ kiểu dữ liệu nào như số, chuỗi, mảng, hàm, hoặc thậm chí là một Object khác.

Ví dụ:

const student = {
  id: 101,               // key: "id", value: 101 (số)
  name: "Alice",         // key: "name", value: "Alice" (chuỗi)
  scores: [85, 90, 78],  // key: "scores", value: [85, 90, 78] (mảng)
  address: {             // key: "address", value: object con
    city: "Hanoi",
    country: "Vietnam"
  },
  greet: function() {    // key: "greet", value: hàm (method)
    return "Hello!";
  }
};

Cách tạo và truy cập thuộc tính trong Object

Tạo Object và thêm thuộc tính

Có nhiều cách để tạo Object và thêm thuộc tính:

Cách 1: Dùng object literal ({})

const car = {
  brand: "Toyota",
  model: "Corolla",
  year: 2022
};

Cách 2: Sử dụng new Object()

const car = new Object();
car.brand = "Toyota";
car.model = "Corolla";
car.year = 2022;

Cách 3: Dùng Object.defineProperty() (Khi cần kiểm soát chi tiết hơn)

const user = {};
Object.defineProperty(user, "username", {
  value: "john_doe",
  writable: false,  // Không thể thay đổi giá trị
  enumerable: true, // Có thể lặp qua
  configurable: false // Không thể xóa hoặc chỉnh sửa
});

Truy cập thuộc tính trong Object

Có hai cách để truy cập thuộc tính của Object:

Dùng dấu chấm (.) – Cách phổ biến nhất

console.log(car.brand); // "Toyota"
console.log(student.name); // "Alice"

Dùng dấu ngoặc vuông ([]) – Khi key là biến hoặc có ký tự đặc biệt

console.log(car["model"]); // "Corolla"

const key = "year";
console.log(car[key]); // 2022

const obj = { "full-name": "John Doe" };
console.log(obj["full-name"]); // "John Doe"

Cập nhật hoặc thêm thuộc tính mới vào Object

car.color = "Red";  // Thêm thuộc tính mới
car.year = 2023;     // Cập nhật giá trị thuộc tính hiện có
console.log(car);

Xóa thuộc tính khỏi Object

delete car.year;
console.log(car); // { brand: "Toyota", model: "Corolla", color: "Red" }

Các loại thuộc tính trong Object JavaScript

Trong JavaScript, thuộc tính của Object có thể chia thành ba loại chính:

Thuộc tính dữ liệu (Data Property)

Đây là loại thuộc tính phổ biến nhất, chứa một giá trị cụ thể và có thể được truy cập hoặc thay đổi trực tiếp.

Ví dụ:

const user = {
  name: "Alice",  // Thuộc tính dữ liệu
  age: 25         // Thuộc tính dữ liệu
};

console.log(user.name); // "Alice"
user.age = 26;          // Cập nhật giá trị
console.log(user.age);  // 26

Thuộc tính dữ liệu có thể chứa bất kỳ kiểu dữ liệu nào như số, chuỗi, boolean, object, function,...

Thuộc tính truy cập (Accessor Property)

Thuộc tính truy cập được định nghĩa bằng getter (get)setter (set) thay vì lưu trữ giá trị trực tiếp.

  • Getter (get): Dùng để truy xuất giá trị thuộc tính.

  • Setter (set): Dùng để thiết lập giá trị thuộc tính.

Ví dụ:

const person = {
  firstName: "John",
  lastName: "Doe",

  // Getter: Tạo thuộc tính fullName dựa trên firstName và lastName
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },

  // Setter: Cập nhật firstName và lastName khi gán fullName
  set fullName(name) {
    const parts = name.split(" ");
    this.firstName = parts[0];
    this.lastName = parts[1];
  }
};

console.log(person.fullName); // "John Doe"

person.fullName = "Alice Smith"; // Gọi setter
console.log(person.firstName); // "Alice"
console.log(person.lastName);  // "Smith"
  • Khi cần tính toán giá trị thuộc tính động dựa trên các thuộc tính khác.

  • Khi muốn kiểm soát logic trước khi gán giá trị vào thuộc tính.

  • Khi muốn tạo thuộc tính chỉ đọc (chỉ có get, không có set).

Thuộc tính kế thừa từ Prototype

Trong JavaScript, các Object có thể kế thừa thuộc tính từ một prototype thông qua prototype chain.

Ví dụ:

const animal = {
  species: "Dog"
};

const pet = Object.create(animal); // pet kế thừa từ animal
pet.name = "Buddy";

console.log(pet.name);    // "Buddy" (thuộc tính riêng của pet)
console.log(pet.species); // "Dog" (thuộc tính kế thừa từ animal)

Lưu ý:

  • Khi truy cập pet.species, JavaScript sẽ kiểm tra Object pet. Nếu không tìm thấy, nó sẽ tìm lên prototype chain và lấy giá trị từ animal.

  • Nếu một Object có cùng thuộc tính với prototype, thuộc tính của Object sẽ được ưu tiên.

Các đặc tính của thuộc tính trong Object Javascript

Mỗi thuộc tính trong JavaScript có bốn đặc tính quan trọng quy định cách nó hoạt động:

Đặc tính Ý nghĩa
writable Có thể thay đổi giá trị của thuộc tính không?
enumerable Có thể liệt kê qua vòng lặp for...inObject.keys() không?
configurable Có thể xóa hoặc thay đổi đặc tính của thuộc tính không?
value Giá trị của thuộc tính (chỉ áp dụng cho Data Property)

Kiểm tra đặc tính của thuộc tính

Dùng Object.getOwnPropertyDescriptor() để kiểm tra:

const user = { name: "Alice" };
console.log(Object.getOwnPropertyDescriptor(user, "name"));

Kết quả:

{
  "value": "Alice",
  "writable": true,
  "enumerable": true,
  "configurable": true
}

Thay đổi đặc tính của thuộc tính

Dùng Object.defineProperty() để điều chỉnh đặc tính của thuộc tính:

Ví dụ: Tạo thuộc tính chỉ đọc (writable: false)

const user = {};
Object.defineProperty(user, "username", {
  value: "john_doe",
  writable: false,  // Không thể thay đổi
  enumerable: true,
  configurable: true
});

console.log(user.username); // "john_doe"
user.username = "alice_smith"; // Không thay đổi được
console.log(user.username); // Vẫn là "john_doe"
Ví dụ: Ẩn thuộc tính khỏi vòng lặp (enumerable: false)
const book = { title: "JavaScript Basics", author: "John Doe" };
Object.defineProperty(book, "author", { enumerable: false });

console.log(Object.keys(book)); // ["title"] (Không hiển thị "author")
Ví dụ: Ngăn chặn xóa hoặc sửa đặc tính thuộc tính (configurable: false)
const user = { role: "admin" };
Object.defineProperty(user, "role", { configurable: false });

// Không thể xóa thuộc tính
delete user.role; 
console.log(user.role); // "admin"

// Không thể thay đổi đặc tính thuộc tính
Object.defineProperty(user, "role", { writable: false }); // Lỗi!

Quản lý thuộc tính trong Object Javascript

Trong JavaScript, có nhiều cách để thêm, cập nhật, xóa và kiểm tra thuộc tính trong Object.

Thêm và cập nhật thuộc tính

Có hai cách phổ biến để thêm hoặc cập nhật thuộc tính của Object:

Cách 1: Sử dụng dấu . (dot notation)

const user = {};  
user.name = "Alice";  // Thêm thuộc tính
user.age = 25;        // Thêm thuộc tính
user.age = 26;        // Cập nhật thuộc tính

console.log(user); // { name: "Alice", age: 26 }

Lưu ý: Chỉ dùng được khi tên thuộc tính là một tên hợp lệ (không chứa khoảng trắng, ký tự đặc biệt, v.v.).

Cách 2: Sử dụng [] (bracket notation)

const user = {};  
user["full name"] = "Alice Smith"; // Tạo thuộc tính có khoảng trắng
user["age"] = 25;  

console.log(user["full name"]); // "Alice Smith"

Ưu điểm của []: Dùng được với tên thuộc tính động hoặc có ký tự đặc biệt.

Ví dụ:

const key = "email";
user[key] = "[email protected]";
console.log(user.email); // "[email protected]"

Xóa thuộc tính

Sử dụng delete để xóa thuộc tính khỏi Object.

const user = { name: "Alice", age: 25 };
delete user.age;  

console.log(user); // { name: "Alice" }

Lưu ý:

  • delete chỉ xóa thuộc tính chứ không giải phóng bộ nhớ.

  • Nếu thuộc tính được kế thừa từ prototype, delete không xóa được.

Kiểm tra thuộc tính trong Object

Cách 1: Dùng hasOwnProperty()

Kiểm tra xem Object có thuộc tính đó không (không tính thuộc tính kế thừa từ prototype).

const user = { name: "Alice" };
console.log(user.hasOwnProperty("name"));  // true
console.log(user.hasOwnProperty("age"));   // false

Cách 2: Dùng in

Toán tử in kiểm tra cả thuộc tính riêng của Object và thuộc tính kế thừa từ prototype.

const user = { name: "Alice" };
console.log("name" in user);   // true
console.log("age" in user);    // false

// Kế thừa từ prototype
console.log("toString" in user); // true (toString là thuộc tính kế thừa)

Khác biệt giữa hasOwnProperty()in:

  • hasOwnProperty() chỉ kiểm tra thuộc tính trực tiếp.

  • in kiểm tra cả thuộc tính kế thừa.

Định nghĩa thuộc tính chi tiết với Object.defineProperty()

Dùng khi muốn kiểm soát các đặc tính của thuộc tính (writable, enumerable, configurable).

Ví dụ: Tạo thuộc tính chỉ đọc (writable: false)

const user = {};
Object.defineProperty(user, "username", {
  value: "john_doe",
  writable: false,  // Không thể thay đổi giá trị
  enumerable: true,
  configurable: true
});

console.log(user.username); // "john_doe"
user.username = "alice_smith"; // Không thay đổi được
console.log(user.username); // "john_doe"

Định nghĩa nhiều thuộc tính cùng lúc với Object.defineProperties()

Dùng để thiết lập nhiều thuộc tính cùng một lúc.

const user = {};
Object.defineProperties(user, {
  name: {
    value: "Alice",
    writable: true,
    enumerable: true,
    configurable: true
  },
  age: {
    value: 25,
    writable: false, // Không thể thay đổi
    enumerable: true,
    configurable: true
  }
});

console.log(user); // { name: "Alice", age: 25 }
user.age = 30; // Không thay đổi được do writable: false
console.log(user.age); // 25

2.5. Đóng băng và bảo vệ thuộc tính trong Object

JavaScript cung cấp các phương thức giúp bảo vệ Object khỏi bị thay đổi:

Object.freeze() – Ngăn chặn mọi thay đổi

  • Không thể thêm mới, sửa đổi hoặc xóa thuộc tính.

  • Object trở thành bất biến.

const user = { name: "Alice", age: 25 };
Object.freeze(user);

user.age = 30;  // Không thay đổi được
user.city = "New York"; // Không thêm mới được
delete user.name; // Không xóa được

console.log(user); // { name: "Alice", age: 25 }
Lưu ý: Object.freeze() chỉ đóng băng các thuộc tính trực tiếp, nếu Object chứa Object con (nested object), Object con vẫn có thể bị thay đổi.
const user = {
  name: "Alice",
  address: { city: "New York" }
};
Object.freeze(user);

user.address.city = "Los Angeles"; // Thay đổi được do object con không bị freeze
console.log(user.address.city); // "Los Angeles"
Cách đóng băng toàn bộ Object (bao gồm nested object):
function deepFreeze(obj) {
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === "object") deepFreeze(obj[key]);
  });
  return Object.freeze(obj);
}
deepFreeze(user);

Object.seal() – Ngăn chặn thêm/xóa, nhưng vẫn có thể chỉnh sửa giá trị

const user = { name: "Alice", age: 25 };
Object.seal(user);

user.age = 30; // Có thể thay đổi
user.city = "New York"; // Không thêm mới được
delete user.name; // Không xóa được

console.log(user); // { name: "Alice", age: 30 }

Khác freeze(): Thuộc tính có thể thay đổi nhưng không thể thêm/xóa.

Object.preventExtensions() – Ngăn chặn thêm thuộc tính mới

  • Vẫn có thể thay đổi và xóa thuộc tính hiện có.

const user = { name: "Alice" };
Object.preventExtensions(user);

user.age = 25; // Không thêm được thuộc tính mới
delete user.name; // Có thể xóa được
console.log(user); // {}

Kết bài

Trong JavaScript, thuộc tính của Object đóng vai trò quan trọng trong việc lưu trữ và quản lý dữ liệu. Chúng không chỉ đơn giản là các cặp key-value, mà còn có thể được kiểm soát thông qua các đặc tính như writable, enumerable, và configurable.

Chúng ta có thể dễ dàng thêm, cập nhật, xóa và kiểm tra thuộc tính bằng các phương thức như Object.defineProperty(), Object.hasOwnProperty() hoặc toán tử in. Đồng thời, JavaScript cũng cung cấp các cơ chế bảo vệ như Object.freeze(), Object.seal(), và Object.preventExtensions() để giới hạn việc thay đổi Object theo nhu cầu.

Việc hiểu và sử dụng hiệu quả các thuộc tính Object giúp lập trình viên viết code rõ ràng, bảo mật hơn và tối ưu hiệu suất trong các ứng dụng JavaScript.

Bài viết liên quan