Object

Baseline 廣泛可用 *

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

* 此特性的某些部分可能存在不同級別的支援。

Object 型別是 JavaScript 資料型別之一。它用於儲存各種帶鍵的集合以及更復雜的實體。可以透過 Object() 建構函式或 物件初始化器/字面量語法來建立物件。

描述

JavaScript 中幾乎所有物件都是 Object 的例項;一個典型的物件會從 Object.prototype 繼承屬性(包括方法),儘管這些屬性可能會被遮蔽(即被覆蓋)。唯一不從 Object.prototype 繼承的物件是那些具有null 原型的物件,或繼承自其他 null 原型物件的物件。

透過原型鏈,對 Object.prototype 物件的更改會影響到所有物件,除非那些受更改影響的屬性和方法在原型鏈中進一步被覆蓋。這提供了一種非常強大但可能危險的機制來覆蓋或擴充套件物件行為。為了使其更安全,Object.prototype 是核心 JavaScript 語言中唯一具有不可變原型的物件——Object.prototype 的原型始終為 null 且不可更改。

Object 原型屬性

應避免直接從例項呼叫任何 Object.prototype 方法,特別是那些不打算多型的方法(即,只有其初始行為有意義,並且沒有派生物件能夠以有意義的方式覆蓋它)。所有從 Object.prototype 派生的物件都可能定義一個具有相同名稱的自定義自有屬性,但其語義與你所期望的完全不同。此外,這些屬性不會被null 原型物件繼承。所有用於處理物件的現代 JavaScript 工具都是靜態的。更具體地說:

在不存在語義等效靜態方法的情況下,或者如果你確實想使用 Object.prototype 方法,你應該直接在目標物件上呼叫() Object.prototype 方法,以防止物件擁有產生意外結果的覆蓋屬性。

js
const obj = {
  foo: 1,
  // You should not define such a method on your own object,
  // but you may not be able to prevent it from happening if
  // you are receiving the object from external input
  propertyIsEnumerable() {
    return false;
  },
};

obj.propertyIsEnumerable("foo"); // false; unexpected result
Object.prototype.propertyIsEnumerable.call(obj, "foo"); // true; expected result

從物件中刪除屬性

物件本身沒有任何方法可以刪除其自身屬性(例如 Map.prototype.delete())。為此,必須使用 delete 運算子。

空原型物件

JavaScript 中的幾乎所有物件最終都繼承自 Object.prototype(參見繼承與原型鏈)。但是,你可以使用 Object.create(null) 或帶 __proto__: null物件字面量語法建立 null 原型物件(注意:物件字面量中的 __proto__ 鍵與已棄用的 Object.prototype.__proto__ 屬性不同)。你也可以透過呼叫 Object.setPrototypeOf(obj, null) 將現有物件的原型更改為 null

js
const obj = Object.create(null);
const obj2 = { __proto__: null };

具有 null 原型的物件可能以意想不到的方式執行,因為它不繼承 Object.prototype 中的任何物件方法。在除錯時尤其如此,因為常見的物件屬性轉換/檢測實用函式可能會產生錯誤或丟失資訊(尤其是使用忽略錯誤的靜默錯誤捕獲時)。

例如,缺少 Object.prototype.toString() 通常使除錯變得棘手

js
const normalObj = {}; // create a normal object
const nullProtoObj = Object.create(null); // create an object with "null" prototype

console.log(`normalObj is: ${normalObj}`); // shows "normalObj is: [object Object]"
console.log(`nullProtoObj is: ${nullProtoObj}`); // throws error: Cannot convert object to primitive value

alert(normalObj); // shows [object Object]
alert(nullProtoObj); // throws error: Cannot convert object to primitive value

其他方法也將失敗。

js
normalObj.valueOf(); // shows {}
nullProtoObj.valueOf(); // throws error: nullProtoObj.valueOf is not a function

normalObj.hasOwnProperty("p"); // shows "true"
nullProtoObj.hasOwnProperty("p"); // throws error: nullProtoObj.hasOwnProperty is not a function

normalObj.constructor; // shows "Object() { [native code] }"
nullProtoObj.constructor; // shows "undefined"

我們可以透過給空原型物件賦值來重新新增 toString 方法

js
nullProtoObj.toString = Object.prototype.toString; // since new object lacks toString, add the original generic one back

console.log(nullProtoObj.toString()); // shows "[object Object]"
console.log(`nullProtoObj is: ${nullProtoObj}`); // shows "nullProtoObj is: [object Object]"

與普通物件中 toString() 位於物件原型上不同,這裡的 toString() 方法是 nullProtoObj 的自有屬性。這是因為 nullProtoObj 沒有(null)原型。

你還可以使用 Object.setPrototypeOf(nullProtoObj, Object.prototype) 將空原型物件恢復為普通物件。

在實踐中,具有 null 原型的物件通常用作 map 的廉價替代品。Object.prototype 屬性的存在會導致一些錯誤

js
const ages = { alice: 18, bob: 27 };

function hasPerson(name) {
  return name in ages;
}

function getAge(name) {
  return ages[name];
}

hasPerson("hasOwnProperty"); // true
getAge("toString"); // [Function: toString]

使用 null 原型物件可以消除這種危險,而不會給 hasPersongetAge 函式引入太多複雜性

js
const ages = Object.create(null, {
  alice: { value: 18, enumerable: true },
  bob: { value: 27, enumerable: true },
});

hasPerson("hasOwnProperty"); // false
getAge("toString"); // undefined

在這種情況下,新增任何方法都應謹慎,因為它們可能與儲存為資料的其他鍵值對混淆。

使你的物件不從 Object.prototype 繼承也可以防止原型汙染攻擊。如果惡意指令碼向 Object.prototype 新增屬性,它將可以在程式中的每個物件上訪問,除了具有 null 原型的物件。

js
const user = {};

// A malicious script:
Object.prototype.authenticated = true;

// Unexpectedly allowing unauthenticated user to pass through
if (user.authenticated) {
  // access confidential data
}

JavaScript 也有內建的 API 可以生成 null 原型物件,特別是那些將物件用作臨時鍵值集合的 API。例如:

null 原型物件”一詞通常也包括原型鏈中不包含 Object.prototype 的任何物件。使用類時,此類物件可以使用 extends null 建立。

物件強制轉換

許多期望物件的內建操作首先將其引數強制轉換為物件。該操作可以總結如下:

在 JavaScript 中,有兩種方法可以實現幾乎相同的效果。

  • Object.prototype.valueOf()Object.prototype.valueOf.call(x) 完全按照上述物件強制轉換步驟將 x 轉換。
  • Object() 函式:Object(x) 使用相同的演算法轉換 x,除了 undefinednull 不會丟擲 TypeError,而是返回一個普通物件。

使用物件強制轉換的地方包括:

  • for...in 迴圈的 object 引數。
  • Array 方法的 this 值。
  • Object 方法的引數,例如 Object.keys()
  • 當在原始值上訪問屬性時進行自動裝箱,因為原始值沒有屬性。
  • 呼叫非嚴格函式時的 this 值。原始值被裝箱,而 nullundefined 被替換為全域性物件

轉換為原始值不同,物件強制轉換過程本身無法以任何方式觀察到,因為它不呼叫像 toStringvalueOf 方法這樣的自定義程式碼。

建構函式

Object()

將輸入轉換為一個物件。

靜態方法

Object.assign()

將一個或多個源物件的所有可列舉自有屬性的值複製到目標物件。

Object.create()

使用指定的原型物件和屬性建立一個新物件。

Object.defineProperties()

向物件新增由給定描述符描述的命名屬性。

Object.defineProperty()

向物件新增由給定描述符描述的命名屬性。

Object.entries()

返回一個數組,其中包含給定物件的所有自有可列舉字串屬性的 [key, value] 對。

Object.freeze()

凍結一個物件。其他程式碼無法刪除或更改其屬性。

Object.fromEntries()

從一個 [key, value] 對的迭代器返回一個新物件。(這與 Object.entries 相反)。

Object.getOwnPropertyDescriptor()

返回物件上命名屬性的屬性描述符。

Object.getOwnPropertyDescriptors()

返回一個物件,其中包含物件的所有自有屬性描述符。

Object.getOwnPropertyNames()

返回一個數組,其中包含給定物件的所有自有可列舉和不可列舉屬性的名稱。

Object.getOwnPropertySymbols()

返回給定物件直接找到的所有 Symbol 屬性的陣列。

Object.getPrototypeOf()

返回指定物件的原型(內部 [[Prototype]] 屬性)。

Object.groupBy()

根據提供的回撥函式返回的字串值,對給定迭代器中的元素進行分組。返回的物件為每個組都有單獨的屬性,其中包含組中的元素陣列。

Object.hasOwn()

如果指定物件具有指示的屬性作為其自有屬性,則返回 true;如果該屬性是繼承的或不存在,則返回 false

Object.is()

比較兩個值是否相同。所有 NaN 值都相等(這與 == 使用的 IsLooselyEqual=== 使用的 IsStrictlyEqual 都不同)。

Object.isExtensible()

確定是否允許擴充套件物件。

Object.isFrozen()

確定物件是否已凍結。

Object.isSealed()

確定物件是否已密封。

Object.keys()

返回一個數組,其中包含給定物件的所有自有可列舉字串屬性的名稱。

Object.preventExtensions()

阻止物件進行任何擴充套件。

Object.seal()

阻止其他程式碼刪除物件的屬性。

Object.setPrototypeOf()

設定物件的原型(其內部 [[Prototype]] 屬性)。

Object.values()

返回一個數組,其中包含給定物件的所有自有可列舉字串屬性對應的值。

例項屬性

這些屬性在 Object.prototype 上定義,並由所有 Object 例項共享。

Object.prototype.__proto__ 已棄用

指向物件例項化時用作原型的物件。

Object.prototype.constructor

建立例項物件的建構函式。對於普通 Object 例項,初始值是 Object 建構函式。其他建構函式的例項都從其各自的 Constructor.prototype 物件繼承 constructor 屬性。

例項方法

Object.prototype.__defineGetter__() 已棄用

將一個函式與一個屬性關聯起來,當訪問該屬性時,執行該函式並返回其返回值。

Object.prototype.__defineSetter__() 已棄用

將一個函式與一個屬性關聯起來,當設定該屬性時,執行該函式以修改該屬性。

Object.prototype.__lookupGetter__() 已棄用

返回繫結為指定屬性的 getter 的函式。

Object.prototype.__lookupSetter__() 已棄用

返回繫結為指定屬性的 setter 的函式。

Object.prototype.hasOwnProperty()

返回一個布林值,指示物件是否包含指定屬性作為該物件的直接屬性,而不是透過原型鏈繼承的屬性。

Object.prototype.isPrototypeOf()

返回一個布林值,指示此方法被呼叫的物件是否在指定物件的原型鏈中。

Object.prototype.propertyIsEnumerable()

返回一個布林值,指示指定的屬性是否是物件的可列舉自有屬性。

Object.prototype.toLocaleString()

呼叫 toString()

Object.prototype.toString()

返回物件的字串表示形式。

Object.prototype.valueOf()

返回指定物件的原始值。

示例

構造空物件

以下示例使用 new 關鍵字和不同引數建立空物件

js
const o1 = new Object();
const o2 = new Object(undefined);
const o3 = new Object(null);

使用 Object() 建構函式將原始值轉換為其各自型別的 Object

你可以使用 Object() 建構函式來建立原始值的物件包裝器。

以下示例建立了變數 o1o2,它們是儲存 BooleanBigInt 值的物件

js
// Equivalent to const o1 = new Boolean(true)
const o1 = new Object(true);

// No equivalent because BigInt() can't be called as a constructor,
// and calling it as a regular function won't create an object
const o2 = new Object(1n);

物件原型

當更改現有 Object.prototype 方法的行為時,請考慮在現有邏輯之前或之後包裝擴充套件來注入程式碼。例如,此(未經測試的)程式碼將在內建邏輯或其他人擴充套件執行之前預先有條件地執行自定義邏輯。

在透過 hook 修改原型時,透過呼叫函式的 apply()this 和引數(呼叫狀態)傳遞給當前行為。這種模式可用於任何原型,例如 Node.prototypeFunction.prototype 等。

js
const current = Object.prototype.valueOf;

// Since my property "-prop-value" is cross-cutting and isn't always
// on the same prototype chain, I want to modify Object.prototype:
Object.prototype.valueOf = function (...args) {
  if (Object.hasOwn(this, "-prop-value")) {
    return this["-prop-value"];
  }
  // It doesn't look like one of my objects, so let's fall back on
  // the default behavior by reproducing the current behavior as best we can.
  // The apply behaves like "super" in some other languages.
  // Even though valueOf() doesn't take arguments, some other hook may.
  return current.apply(this, args);
};

警告: 修改任何內建建構函式的 prototype 屬性都被認為是不好的做法,並且存在前向相容性風險。

你可以在繼承與原型鏈中閱讀更多關於原型的資訊。

規範

規範
ECMAScript® 2026 語言規範
# sec-object-objects

瀏覽器相容性

另見