对象
**Object
** 类型表示 JavaScript 数据类型 之一。它用于存储各种键控集合和更复杂的实体。可以使用 Object()
构造函数或 对象初始化程序/字面量语法 创建对象。
描述
JavaScript 中几乎所有 对象 都是 Object
的实例;一个典型的对象继承了 Object.prototype
的属性(包括方法),尽管这些属性可能会被隐藏(即被覆盖)。唯一不继承自 Object.prototype
的对象是具有 null
原型 或从其他 null
原型对象派生的对象。
对 Object.prototype
对象的更改会通过原型链被 **所有** 对象看到,除非这些更改所涉及的属性和方法在原型链中进一步被覆盖。这提供了一种非常强大但可能存在危险的机制来覆盖或扩展对象行为。为了使其更安全,Object.prototype
是核心 JavaScript 语言中唯一具有 不可变原型 的对象——Object.prototype
的原型始终为 null
且不可更改。
对象原型属性
您应该避免从实例中直接调用任何 Object.prototype
方法,尤其是那些不打算多态的方法(即只有其初始行为才有意义,并且没有后代对象可以以有意义的方式覆盖它)。所有从 Object.prototype
派生的对象都可以定义一个具有相同名称的自定义自身属性,但其语义与您期望的完全不同。此外,这些属性不会被 null
原型对象 继承。所有用于处理对象的现代 JavaScript 实用程序都是 静态的。更具体地说
valueOf()
、toString()
和toLocaleString()
存在是为了多态,您应该期望对象定义自己的实现并具有合理的行为,以便您可以将其作为实例方法调用。但是,valueOf()
和toString()
通常通过 类型转换 隐式调用,您不需要在代码中自己调用它们。__defineGetter__()
、__defineSetter__()
、__lookupGetter__()
和__lookupSetter__()
已弃用,不应使用。请改用静态替代方法Object.defineProperty()
和Object.getOwnPropertyDescriptor()
。__proto__
属性已弃用,不应使用。Object.getPrototypeOf()
和Object.setPrototypeOf()
替代方案是静态方法。propertyIsEnumerable()
和hasOwnProperty()
方法可以分别替换为Object.getOwnPropertyDescriptor()
和Object.hasOwn()
静态方法。isPrototypeOf()
方法通常可以使用instanceof
替换,如果您正在检查构造函数的prototype
属性。
在语义上等效的静态方法不存在的情况下,或者如果您确实想要使用 Object.prototype
方法,您应该直接 call()
目标对象上的 Object.prototype
方法,以防止对象具有覆盖属性从而产生意外结果。
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
。
const obj = Object.create(null);
const obj2 = { __proto__: null };
具有 null
原型的对象的行为可能会出乎意料,因为它不会从 Object.prototype
继承任何对象方法。在调试时尤其如此,因为常见对象属性转换/检测实用程序函数可能会生成错误或丢失信息(尤其是在使用忽略错误的静默错误陷阱时)。
例如,缺少 Object.prototype.toString()
通常会使调试变得难以处理
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
其他方法也会失败。
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
方法来将其添加回空原型对象。
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
原型的对象通常用作映射的廉价替代品。Object.prototype
属性的存在会导致一些错误。
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]
使用无原型对象消除了这种风险,而不会给hasPerson
和getAge
函数带来过多的复杂性。
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
添加了一个属性,则它将可以在程序中的每个对象上访问,除了具有空原型的对象。
const user = {};
// A malicious script:
Object.prototype.authenticated = true;
// Unexpectedly allowing unauthenticated user to pass through
if (user.authenticated) {
// access confidential data
}
JavaScript还内置了生成null
原型对象的API,特别是那些使用对象作为临时键值集合的API。例如
Object.groupBy()
的返回值RegExp.prototype.exec()
结果的groups
和indices.groups
属性Array.prototype[Symbol.unscopables]
(所有[Symbol.unscopables]
对象都应具有null
原型)import.meta
- 通过
import * as ns from "module";
或import()
获得的模块命名空间对象。
术语“null
原型对象”通常还包括任何在其原型链中没有Object.prototype
的对象。使用类时,可以使用extends null
创建此类对象。
对象强制转换
许多期望对象的内置操作首先将其参数强制转换为对象。该操作可以概括如下
在JavaScript中可以通过两种方法实现几乎相同的效果。
Object.prototype.valueOf()
:Object.prototype.valueOf.call(x)
完全执行上面解释的对象强制转换步骤以转换x
。Object()
函数:Object(x)
使用相同的算法转换x
,除了undefined
和null
不会抛出TypeError
,而是返回一个普通对象。
使用对象强制转换的位置包括
for...in
循环的object
参数。Array
方法的this
值。Object
方法(如Object.keys()
)的参数。- 当访问基本类型上的属性时进行自动装箱,因为基本类型没有属性。
- 调用非严格函数时的
this
值。基本类型会被装箱,而null
和undefined
会被替换为全局对象。
与转换为基本类型不同,对象强制转换过程本身无法以任何方式观察到,因为它不会像toString
或valueOf
方法那样调用自定义代码。
构造函数
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()
-
返回在给定对象上直接找到的所有符号属性的数组。
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
关键字和不同的参数创建空对象。
const o1 = new Object();
const o2 = new Object(undefined);
const o3 = new Object(null);
使用Object()构造函数将基本类型转换为其相应类型的Object
您可以使用Object()
构造函数创建基本值的Object包装器。
以下示例创建变量o1
和o2
,它们是存储Boolean
和BigInt
值的Object。
// 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
方法的行为时,请考虑通过在现有逻辑之前或之后包装扩展来注入代码。例如,此(未经测试)代码将在内置逻辑或其他人的扩展执行之前执行自定义逻辑。
在使用钩子修改原型时,通过对函数调用apply()
将this
和参数(调用状态)传递给当前行为。此模式可用于任何原型,例如Node.prototype
、Function.prototype
等。
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"];
} else {
// 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语言规范 # sec-object-objects |
浏览器兼容性
BCD表格仅在启用JavaScript的浏览器中加载。