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

浏览器兼容性

另见