Object initializer

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.

Object initializer (khởi tạo đối tượng) là danh sách các cặp tên thuộc tính và giá trị tương ứng của đối tượng, được phân tách bằng dấu phẩy và đặt trong cặp dấu ngoặc nhọn ({}), có thể có không hoặc nhiều cặp. Đối tượng cũng có thể được khởi tạo bằng Object.create() hoặc bằng cách gọi hàm constructor với toán tử new.

Try it

const object1 = { a: "foo", b: 42, c: {} };

console.log(object1.a);
// Expected output: "foo"

const a = "foo";
const b = 42;
const c = {};
const object2 = { a: a, b: b, c: c };

console.log(object2.b);
// Expected output: 42

const object3 = { a, b, c };

console.log(object3.a);
// Expected output: "foo"

Cú pháp

js
o = {
  a: "foo",
  b: 42,
  c: {},
  1: "number literal property",
  "foo:bar": "string literal property",

  shorthandProperty,

  method(parameters) {
    // …
  },

  get property() {},
  set property(value) {},

  [expression]: "computed property",

  __proto__: prototype,

  ...spreadProperty,
};

Mô tả

Object initializer là một biểu thức mô tả việc khởi tạo một Object. Đối tượng bao gồm các thuộc tính (properties), được dùng để mô tả đối tượng đó. Giá trị của thuộc tính đối tượng có thể chứa dữ liệu kiểu nguyên thủy hoặc các đối tượng khác.

Cú pháp object literal so với JSON

Cú pháp object literal không giống với JavaScript Object Notation (JSON). Mặc dù trông có vẻ tương tự, nhưng có những điểm khác biệt giữa chúng:

  • JSON chỉ cho phép định nghĩa thuộc tính theo cú pháp "property": value. Tên thuộc tính phải được đặt trong dấu nháy kép, và định nghĩa không thể là dạng rút gọn. Tên thuộc tính được tính toán (computed) cũng không được phép.
  • Giá trị thuộc tính của đối tượng JSON chỉ có thể là chuỗi, số, true, false, null, mảng, hoặc một đối tượng JSON khác. Điều này có nghĩa là JSON không thể biểu diễn các phương thức hoặc các đối tượng không thuần túy như Map hay RegExp.
  • Trong JSON, "__proto__" là một khóa thuộc tính bình thường. Trong object literal, nó thiết lập prototype của đối tượng.

JSON là một tập con nghiêm ngặt của cú pháp object literal, có nghĩa là mọi văn bản JSON hợp lệ đều có thể được phân tích cú pháp như một object literal và thường không gây ra lỗi cú pháp. Ngoại lệ duy nhất là cú pháp object literal cấm các khóa __proto__ trùng lặp, điều này không áp dụng với JSON.parse(). Hàm này xử lý __proto__ như một thuộc tính bình thường và lấy lần xuất hiện cuối cùng làm giá trị của thuộc tính. Trường hợp duy nhất mà giá trị đối tượng mà chúng biểu diễn (hay còn gọi là ngữ nghĩa) khác nhau là khi nguồn chứa khóa __proto__ — đối với object literal, nó thiết lập prototype của đối tượng; còn đối với JSON, đó là thuộc tính bình thường.

js
console.log(JSON.parse('{ "__proto__": 0, "__proto__": 1 }')); // {__proto__: 1}
console.log({ "__proto__": 0, "__proto__": 1 }); // SyntaxError: Duplicate __proto__ fields are not allowed in object literals

console.log(JSON.parse('{ "__proto__": {} }')); // { __proto__: {} }
console.log({ "__proto__": {} }); // {} (with {} as prototype)

Ví dụ

Tạo đối tượng

Một đối tượng rỗng không có thuộc tính có thể được tạo như sau:

js
const object = {};

Tuy nhiên, ưu điểm của ký hiệu literal hay initializer là bạn có thể nhanh chóng tạo đối tượng với các thuộc tính bên trong cặp dấu ngoặc nhọn. Bạn ký hiệu một danh sách các cặp key: value được phân tách bằng dấu phẩy.

Đoạn code sau tạo một đối tượng với ba thuộc tính và các khóa là "foo", "age""baz". Giá trị của các khóa này lần lượt là chuỗi "bar", số 42, và một đối tượng khác.

js
const object = {
  foo: "bar",
  age: 42,
  baz: { myProp: 12 },
};

Truy cập thuộc tính

Khi đã tạo đối tượng, bạn có thể muốn đọc hoặc thay đổi chúng. Các thuộc tính đối tượng có thể được truy cập bằng ký hiệu dấu chấm hoặc ký hiệu ngoặc vuông. (Xem property accessors để biết thêm thông tin chi tiết.)

js
object.foo; // "bar"
object["age"]; // 42
object.baz; // {myProp: 12}
object.baz.myProp; // 12

Định nghĩa thuộc tính

Chúng ta đã biết cách ký hiệu thuộc tính bằng cú pháp initializer. Thông thường, có những biến trong code mà bạn muốn đưa vào một đối tượng. Bạn sẽ thấy code như thế này:

js
const a = "foo";
const b = 42;
const c = {};

const o = {
  a: a,
  b: b,
  c: c,
};

Có một ký hiệu ngắn gọn hơn để đạt được điều tương tự:

js
const a = "foo";
const b = 42;
const c = {};

// Shorthand property names
const o = { a, b, c };

// In other words,
console.log(o.a === { a }.a); // true

Tên thuộc tính trùng lặp

Khi sử dụng cùng tên cho các thuộc tính, thuộc tính thứ hai sẽ ghi đè thuộc tính đầu tiên.

js
const a = { x: 1, x: 2 };
console.log(a); // {x: 2}

Sau ES2015, tên thuộc tính trùng lặp được cho phép ở mọi nơi, bao gồm cả strict mode. Bạn cũng có thể có tên thuộc tính trùng lặp trong classes. Ngoại lệ duy nhất là private elements, phải là duy nhất trong thân class.

Định nghĩa phương thức

Thuộc tính của một đối tượng cũng có thể tham chiếu đến một hàm hoặc phương thức getter hay setter.

js
const o = {
  property: function (parameters) {},
  get property() {
    return 1;
  },
  set property(value) {},
};

Có một ký hiệu rút gọn, để từ khóa function không còn cần thiết nữa.

js
// Shorthand method names
const o = {
  property(parameters) {},
};

Cũng có một cách để định nghĩa ngắn gọn các phương thức generator.

js
const o = {
  *generator() {
    // …
  },
};

Tương đương với ký hiệu kiểu ES5 này (nhưng lưu ý rằng ECMAScript 5 không có generator):

js
const o = {
  generator: function* () {
    // …
  },
};

Để biết thêm thông tin và ví dụ về phương thức, hãy xem method definitions.

Tên thuộc tính được tính toán

Cú pháp object initializer cũng hỗ trợ tên thuộc tính được tính toán (computed property names). Điều này cho phép bạn đặt một biểu thức trong cặp dấu ngoặc vuông [], sẽ được tính toán và dùng làm tên thuộc tính. Điều này gợi nhớ đến ký hiệu ngoặc vuông của cú pháp property accessor, mà bạn có thể đã dùng để đọc và thiết lập các thuộc tính.

Giờ bạn cũng có thể dùng cú pháp tương tự trong object literal:

js
// Computed property names
let i = 0;
const a = {
  [`foo${++i}`]: i,
  [`foo${++i}`]: i,
  [`foo${++i}`]: i,
};

console.log(a.foo1); // 1
console.log(a.foo2); // 2
console.log(a.foo3); // 3

const items = ["A", "B", "C"];
const obj = {
  [items]: "Hello",
};
console.log(obj); // A,B,C: "Hello"
console.log(obj["A,B,C"]); // "Hello"

const param = "size";
const config = {
  [param]: 12,
  [`mobile${param.charAt(0).toUpperCase()}${param.slice(1)}`]: 4,
};

console.log(config); // {size: 12, mobileSize: 4}

Thuộc tính spread

Object literal hỗ trợ cú pháp spread. Nó sao chép các thuộc tính enumerable riêng từ một đối tượng được cung cấp vào một đối tượng mới.

Sao chép nông (shallow clone, không bao gồm prototype) hoặc hợp nhất đối tượng giờ đây có thể thực hiện với cú pháp ngắn gọn hơn so với Object.assign().

js
const obj1 = { foo: "bar", x: 42 };
const obj2 = { foo: "baz", y: 13 };

const clonedObj = { ...obj1 };
// { foo: "bar", x: 42 }

const mergedObj = { ...obj1, ...obj2 };
// { foo: "baz", x: 42, y: 13 }

Warning: Lưu ý rằng Object.assign() kích hoạt setters, trong khi cú pháp spread thì không!

Prototype setter

Một định nghĩa thuộc tính có dạng __proto__: value hoặc "__proto__": value không tạo ra thuộc tính có tên __proto__. Thay vào đó, nếu giá trị được cung cấp là một đối tượng hoặc null, nó sẽ trỏ [[Prototype]] của đối tượng được tạo đến giá trị đó. (Nếu giá trị không phải là đối tượng hay null, đối tượng sẽ không bị thay đổi.)

Lưu ý rằng khóa __proto__ là cú pháp được chuẩn hóa, trái ngược với các accessor Object.prototype.__proto__ không chuẩn và không hiệu quả. Nó thiết lập [[Prototype]] trong quá trình tạo đối tượng, tương tự như Object.create — thay vì thay đổi chuỗi prototype.

js
const obj1 = {};
console.log(Object.getPrototypeOf(obj1) === Object.prototype); // true

const obj2 = { __proto__: null };
console.log(Object.getPrototypeOf(obj2)); // null

const protoObj = {};
const obj3 = { "__proto__": protoObj };
console.log(Object.getPrototypeOf(obj3) === protoObj); // true

const obj4 = { __proto__: "not an object or null" };
console.log(Object.getPrototypeOf(obj4) === Object.prototype); // true
console.log(Object.hasOwn(obj4, "__proto__")); // false

Chỉ được phép có một prototype setter trong một object literal. Nhiều prototype setter là lỗi cú pháp.

Các định nghĩa thuộc tính không sử dụng ký hiệu "dấu hai chấm" không phải là prototype setter. Chúng là các định nghĩa thuộc tính hoạt động giống hệt với các định nghĩa tương tự sử dụng bất kỳ tên nào khác.

js
const __proto__ = "variable";

const obj1 = { __proto__ };
console.log(Object.getPrototypeOf(obj1) === Object.prototype); // true
console.log(Object.hasOwn(obj1, "__proto__")); // true
console.log(obj1.__proto__); // "variable"

const obj2 = { __proto__() { return "hello"; } };
console.log(obj2.__proto__()); // "hello"

const obj3 = { ["__proto__"]: 17 };
console.log(obj3.__proto__); // 17

// Mixing prototype setter with normal own properties with "__proto__" key
const obj4 = { ["__proto__"]: 17, __proto__: {} }; // {__proto__: 17} (with {} as prototype)
const obj5 = {
  ["__proto__"]: 17,
  __proto__: {},
  __proto__: null, // SyntaxError: Duplicate __proto__ fields are not allowed in object literals
};
const obj6 = {
  ["__proto__"]: 17,
  ["__proto__"]: "hello",
  __proto__: null,
}; // {__proto__: "hello"} (with null as prototype)
const obj7 =  {
  ["__proto__"]: 17,
  __proto__,
  __proto__: null,
}; // {__proto__: "variable"} (with null as prototype)

Thông số kỹ thuật

Specification
ECMAScript® 2027 Language Specification
# sec-object-initializer

Tương thích trình duyệt

Xem thêm