Mã mô tả cơ sở lưu trú

Phần lớn hoạt động tương tác của bạn với các thuộc tính đối tượng sẽ diễn ra ở cấp nền tảng, bao gồm cả việc tạo giá trị cố định đối tượng cũng như cài đặt và truy cập các giá trị thuộc tính bằng khoá. Tuy nhiên, bạn có thể định cấu hình nội bộ bất kỳ thuộc tính nào của một đối tượng để kiểm soát chi tiết cách các thuộc tính đó được truy cập, thay đổi và xác định. Mỗi thuộc tính đối tượng có một tập hợp các thuộc tính ẩn chứa siêu dữ liệu được liên kết với thuộc tính đó, được gọi là "mô tả thuộc tính".

Có 2 loại mã mô tả liên kết với tài sản bất kỳ: mã mô tả dữ liệumã mô tả trình truy cập. Bộ mô tả dữ liệu bao gồm các cặp khoá và giá trị chứa giá trị của một thuộc tính, bất kể giá trị đó là có thể ghi, có thể định cấu hình hay có thể liệt kê. Bộ mô tả trình truy cập chứa các hàm thực thi khi một thuộc tính được đặt, thay đổi hoặc truy cập.

Tài sản Loại phần mô tả Giá trị mặc định từ
Object.defineProperty()
Nội dung mô tả
[[Value]] Dữ liệu undefined Chứa giá trị của một thuộc tính.
[[Writable]] Dữ liệu false Xác định xem bạn có thể thay đổi giá trị của thuộc tính hay không.
[[Get]] Trình truy cập undefined Hàm getter của thuộc tính. Hàm này sẽ thực thi khi người dùng truy cập vào thuộc tính này.
[[Set]] Trình truy cập undefined Hàm setter của thuộc tính. Hàm này sẽ thực thi khi bạn đặt hoặc thay đổi thuộc tính.
[[Configurable]] Cả hai false Nếu giá trị là false, bạn sẽ không thể xoá và các thuộc tính của thuộc tính đó. Nếu giá trị này là false[[Writable]]true, thì bạn vẫn có thể thay đổi giá trị của thuộc tính đó.
[[Enumerable]] Cả hai false Nếu đây là true, bạn có thể lặp lại thuộc tính bằng cách sử dụng vòng lặp for...in hoặc phương thức tĩnh Object.keys().

Mỗi thuộc tính trong số này đều sử dụng cách viết tắt giống như [[Prototype]], cho biết rằng bạn không thể truy cập trực tiếp vào các thuộc tính này. Thay vào đó, hãy sử dụng phương thức tĩnh Object.defineProperty() để xác định hoặc sửa đổi các thuộc tính của một đối tượng. Object.defineProperty() chấp nhận 3 đối số: đối tượng để thao tác, khoá thuộc tính sẽ được tạo hoặc sửa đổi và một đối tượng chứa(các) mã mô tả liên kết với thuộc tính đang được tạo hoặc sửa đổi.

Theo mặc định, các thuộc tính được tạo bằng Object.defineProperty() không thể ghi, có thể liệt kê hoặc có thể định cấu hình. Tuy nhiên, mọi thuộc tính bạn tạo dưới dạng một phần của giá trị cố định đối tượng hoặc sử dụng ký hiệu dấu chấm hoặc dấu ngoặc đều có thể ghi, có thể liệt kê và có thể định cấu hình.

const myObj = {};

Object.defineProperty(myObj, 'myProperty', {
  value: true,
  writable: false
});

myObj.myProperty;
> true

myObj.myProperty = false;

myObj.myProperty;
> true

Ví dụ: khi [[Writable]] có giá trị false, việc cố gắng đặt một giá trị mới cho thuộc tính được liên kết sẽ tự động không thành công bên ngoài chế độ nghiêm ngặt và sẽ gửi lỗi ở chế độ nghiêm ngặt:

{
    const myObj = {};

    Object.defineProperty(myObj, 'myProperty', {
    value: true,
    writable: false
    });

    myObj.myProperty = false;
    myObj.myProperty;
}
> true

(function () {
    "use strict";
    const myObj = {};

    Object.defineProperty(myObj, 'myProperty', {
    value: true,
    writable: false
    });

    myObj.myProperty = false;
    myObj.myProperty;
}());\
> Uncaught TypeError: "myProperty" is read-only

Việc sử dụng hiệu quả các phần mô tả là một khái niệm khá nâng cao. Tuy nhiên, việc hiểu được cấu trúc bên trong của một đối tượng là cần thiết để hiểu các cú pháp liên quan đến việc xử lý các đối tượng theo những cách phổ biến hơn. Ví dụ: các khái niệm này xuất hiện khi bạn sử dụng phương thức tĩnh Object.create(). Phương thức này cho phép bạn kiểm soát chi tiết mọi nguyên mẫu được đính kèm vào đối tượng mới.

Object.create() tạo một đối tượng mới bằng cách sử dụng một đối tượng hiện có làm nguyên mẫu. Điều này cho phép đối tượng mới kế thừa các thuộc tính và phương thức từ một đối tượng khác do người dùng xác định, giống như cách mà các đối tượng kế thừa thuộc tính từ nguyên mẫu Object tích hợp của JavaScript. Khi gọi Object.create() với một đối tượng làm đối số, thao tác này sẽ tạo một đối tượng trống có đối tượng đã truyền làm nguyên mẫu.

const myCustomPrototype = {
  'myInheritedProp': 10
};

const newObject = Object.create( myCustomPrototype );

newObject;
> Object {  }
<prototype>: Object { myInheritedProp: 10 }
  myInheritedProp: 10
  <prototype>: Object { … }

Object.create có thể lấy đối số thứ hai chỉ định các thuộc tính riêng cho đối tượng mới tạo bằng cách sử dụng cú pháp tương tự như Object.defineProperty() – tức là một khoá ánh xạ đối tượng đến một tập hợp các thuộc tính mô tả:

const myCustomPrototype = {
  'myInheritedProp': 10
};

const myObj = Object.create( myCustomPrototype, {
        myProperty: {
            value: "The new property value.",
            writable: true,
            configurable: true
        }
  });

myObj;
> Object { … }
    myProperty: "The new property value."
    <prototype>: Object { myInheritedProp: 10 }

Trong ví dụ này, đối tượng mới (myObj) sử dụng giá trị cố định đối tượng (myCustomPrototype) làm nguyên mẫu. Bản thân đối tượng này chứa Object.prototype kế thừa, dẫn đến một loạt nguyên mẫu kế thừa được gọi là chuỗi nguyên mẫu. Mỗi đối tượng đều có một nguyên mẫu, cho dù được chỉ định hay kế thừa, trong đó có một nguyên mẫu được chỉ định hoặc kế thừa của riêng đối tượng đó. Chuỗi này kết thúc ở một nguyên mẫu null không có nguyên mẫu nào.

const myPrototype = {
  'protoProp': 10
};

const newObject = Object.setPrototypeOf( { 'objProp' : true }, myPrototype );

newObject;
> Object { objProp: true }
    objProp: true
    <prototype>: Object { protoProp: 10 }
        protoProp: 10
        <prototype>: Object { … }

Các thuộc tính có trong nguyên mẫu của một giá trị có sẵn ở "cấp cao nhất" của một đối tượng mà không cần truy cập trực tiếp vào thuộc tính nguyên mẫu:

const objectLiteral = {
    "value" : true
};

objectLiteral;
> Object { value: true }
    value: true
    <prototype>: Object { … }

objectLiteral.toString();
"[object Object]"

Mẫu này đúng đối với toàn bộ chuỗi nguyên mẫu liên kết với một đối tượng: khi cố gắng truy cập vào một thuộc tính, trình phiên dịch sẽ tìm thuộc tính đó ở mỗi "cấp" của chuỗi nguyên mẫu, từ trên xuống dưới, cho đến khi tìm thấy thuộc tính hoặc chuỗi kết thúc:

const myCustomPrototype = {
  'protoProp': "Prototype property value."
};

const myObj = Object.create( myCustomPrototype, {
    myProperty: {
        value: "Top-level property value.",
        writable: true,
        configurable: true
    }
});

myObj.protoProp;
> "Prototype property value."

Kiểm tra kiến thức

Những phần mô tả nào là trình truy cập?

[[Get]]
[[Set]]
[[Writable]]