Object 总结
基于 ECMA-262 规范总结,包含核心概念、API 详解和实战应用。
一、Object 概述
1.1 什么是 Object
Object 是 JavaScript 中最核心的数据类型:
// 创建对象的方式
const obj = {} // 字面量
const obj2 = new Object() // 构造函数
const obj3 = Object.create(null) // 创建无原型对象1.2 Object 在规范中的地位
- Object 是
%Object%,JavaScript 中所有对象的基类 - 是全局对象的
Object属性 - 可以作为 class 的 extends 子句
class MyClass extends Object {
constructor() {
super()
}
}二、Object 构造函数
2.1 作为函数调用 vs 作为构造函数
// 作为函数:类型转换
Object(5) // Number {5}
Object("hello") // String {"hello"}
Object(true) // Boolean {true}
Object(null) // TypeError (规范规定)
Object(undefined) // TypeError (规范规定)
// 作为构造函数:创建新对象
new Object() // {}
new Object(5) // Number {5}关键点:Object(value) 等价于 ToObject(value)
三、Object 静态方法 (Object.xxx)
3.1 创建对象
Object.create(proto, properties?)
创建新对象,指定原型和属性
// 创建空对象,指定原型为 null(无原型对象)
const obj = Object.create(null) // 干净的对象,没有 toString 等方法
// 创建对象并设置原型
const proto = { greet() { return 'hi' } }
const child = Object.create(proto)
child.greet() // 'hi'
// 同时设置属性
const obj = Object.create({}, {
name: { value: 'John', writable: true, enumerable: true, configurable: true },
age: { value: 30 }
})注意:第二个参数的属性描述符必须显式设置 enumerable 等属性,否则默认都是 false
Object.assign(target, ...sources)
复制源对象的可枚举自有属性到目标对象
// 浅拷贝
const target = { a: 1 }
const source = { b: 2, c: 3 }
Object.assign(target, source) // { a: 1, b: 2, c: 3 }
// 合并多个对象
const merged = Object.assign({}, { a: 1 }, { b: 2 }, { a: 3 })
// { a: 3, b: 2 } 后面覆盖前面
// 复制数组(会转为对象索引)
Object.assign([], { 0: 'a', 1: 'b' }) // ['a', 'b']注意:只拷贝可枚举的自有属性,symbol 也会拷贝
Object.fromEntries(iterable)
将键值对数组/Map 转换为对象(Object.create 的逆操作)
// 从二维数组
Object.fromEntries([['a', 1], ['b', 2]]) // { a: 1, b: 2 }
// 从 Map
const map = new Map([['a', 1], ['b', 2]])
Object.fromEntries(map) // { a: 1, b: 2 }
// 配合 Object.entries 使用
const obj = { a: 1, b: 2 }
Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k, v * 2])
) // { a: 2, b: 4 }
// 对象深拷贝(简单实现)
const deepClone = obj => Object.fromEntries(
Object.entries(obj).map(([k, v]) =>
[k, typeof v === 'object' && v ? deepClone(v) : v]
)
)3.2 属性操作
Object.defineProperty(obj, prop, descriptor)
定义/修改单个属性的描述符
const obj = {}
// 定义数据属性
Object.defineProperty(obj, 'name', {
value: 'John',
writable: false, // 不可修改
enumerable: false, // 不可枚举(不在 for...in 中)
configurable: false // 不可删除/重新定义
})
// 定义访问器属性
let _age = 30
Object.defineProperty(obj, 'age', {
get() { return _age },
set(v) { _age = v },
enumerable: true,
configurable: true
})属性描述符:
| 属性 | 数据属性 | 访问器属性 |
|---|---|---|
value | ✓ | - |
writable | ✓ | - |
get | - | ✓ |
set | - | ✓ |
enumerable | ✓ | ✓ |
configurable | ✓ | ✓ |
Object.defineProperties(obj, properties)
批量定义多个属性
Object.defineProperties(obj, {
name: { value: 'John', enumerable: true },
age: {
get() { return this._age },
set(v) { this._age = v }
}
})Object.getOwnPropertyDescriptor(obj, prop)
获取属性的描述符
const obj = { name: 'John' }
Object.getOwnPropertyDescriptor(obj, 'name')
// { value: 'John', writable: true, enumerable: true, configurable: true }
// 访问器属性
let _age = 30
Object.defineProperty(obj, 'age', { get: () => _age })
Object.getOwnPropertyDescriptor(obj, 'age')
// { get: [Function: get], set: [Function: set], enumerable: false, configurable: false }Object.getOwnPropertyDescriptors(obj)
获取所有属性的描述符
const obj = { a: 1 }
Object.getOwnPropertyDescriptors(obj)
// { a: { value: 1, writable: true, enumerable: true, configurable: true } }应用:浅拷贝(包含属性描述符)
const clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj))Object.getOwnPropertyNames(obj)
获取所有自有属性名(字符串)
const obj = { a: 1, b: 2 }
Object.defineProperty(obj, 'hidden', { value: 'secret', enumerable: false })
Object.getOwnPropertyNames(obj) // ['a', 'b', 'hidden']Object.getOwnPropertySymbols(obj)
获取所有自有 Symbol 属性
const sym = Symbol('mySymbol')
const obj = { [sym]: 'value', a: 1 }
Object.getOwnPropertySymbols(obj) // [Symbol(mySymbol)]3.3 原型操作
Object.getPrototypeOf(obj)
获取对象的原型
const proto = { method() {} }
const obj = Object.create(proto)
Object.getPrototypeOf(obj) === proto // true
Object.getPrototypeOf({}) // Object.prototype
Object.getPrototypeOf([]) // Array.prototypeObject.setPrototypeOf(obj, proto)
设置对象的原型
const proto = { method() {} }
const obj = {}
// 旧方式(不推荐,影响性能)
Object.setPrototypeOf(obj, proto)
// 现代更推荐:Object.create 方式创建时指定原型
const obj2 = Object.create(proto)注意:改变已存在对象的原型开销很大,应尽量在创建时指定
3.4 枚举操作
Object.keys(obj)
获取可枚举的自有属性名(字符串)
const obj = { a: 1, b: 2 }
Object.defineProperty(obj, 'c', { value: 3, enumerable: false })
Object.keys(obj) // ['a', 'b']Object.values(obj)
获取可枚举的自有属性值
const obj = { a: 1, b: 2, c: 3 }
Object.values(obj) // [1, 2, 3]Object.entries(obj)
获取可枚举的自有键值对
const obj = { a: 1, b: 2 }
Object.entries(obj) // [['a', 1], ['b', 2]]
// 转为 Map
new Map(Object.entries(obj)) // Map { 'a' => 1, 'b' => 2 }3.5 属性存在性检查
Object.hasOwn(obj, prop)
检查属性是否为自有属性(现代替代 hasOwnProperty)
const proto = { inherited: 1 }
const obj = Object.create(proto)
obj.own = 2
obj.hasOwnProperty('inherited') // false
Object.hasOwn(obj, 'inherited') // false (更安全,不依赖原型链)
obj.hasOwnProperty('own') // true
Object.hasOwn(obj, 'own') // true为什么用 Object.hasOwn:避免原型链上同名方法的风险
const obj = { hasOwnProperty: 'oops' }
// obj.hasOwnProperty('x') // TypeError!
Object.hasOwn(obj, 'x') // 正确3.6 比较操作
Object.is(value1, value2)
判断两个值是否相同(比 === 更精确)
Object.is(5, 5) // true
Object.is(5, '5') // false
Object.is(NaN, NaN) // true (=== 返回 false!)
Object.is(0, -0) // false (=== 返回 true!)
Object.is(+0, -0) // false
// === 的问题
NaN === NaN // false
0 === -0 // true3.7 完整性保护(冻结/密封/扩展)
Object.freeze(obj)
冻结对象:不可添加/删除/修改属性,不可修改原型
const obj = { a: 1 }
Object.freeze(obj)
obj.a = 2 // 静默失败(非严格模式)或 TypeError(严格模式)
obj.b = 3 // 同上
delete obj.a // 同上
Object.isFrozen(obj) // true应用:
// 冻结配置对象
const config = Object.freeze({
apiUrl: 'https://api.example.com',
maxRetries: 3
})
// 冻结数组
const arr = Object.freeze([1, 2, 3])
// arr.push(4) // TypeErrorObject.seal(obj)
密封对象:不可添加/删除属性,但可修改现有属性
const obj = { a: 1 }
Object.seal(obj)
obj.a = 2 // 可以
obj.b = 3 // 不行
delete obj.a // 不行
Object.isSealed(obj) // trueObject.preventExtensions(obj)
阻止添加新属性(可删除/修改现有属性)
const obj = { a: 1 }
Object.preventExtensions(obj)
obj.b = 2 // 不行
obj.a = 2 // 可以
delete obj.a // 可以
Object.isExtensible(obj) // falseObject.isFrozen(obj) / Object.isSealed(obj) / Object.isExtensible(obj)
检查对象状态
const obj = {}
Object.isExtensible(obj) // true
Object.isSealed(obj) // false
Object.isFrozen(obj) // false
Object.freeze(obj)
Object.isFrozen(obj) // true
Object.isSealed(obj) // true
Object.isExtensible(obj) // false3.8 分组操作
Object.groupBy(items, callback)
按回调返回值对数组元素分组(较新API)
const arr = [1, 2, 3, 4, 5, 6]
Object.groupBy(arr, x => x % 2 === 0 ? 'even' : 'odd')
// { even: [2, 4, 6], odd: [1, 3, 5] }
Object.groupBy(['apple', 'banana', 'cherry'], s => s.length)
// { 5: ['apple'], 6: ['banana', 'cherry'] }返回对象特点:
- 不继承自
%Object.prototype% - 值是数组
四、Object 原型方法 (obj.xxx)
4.1 基础方法
obj.hasOwnProperty(v)
检查属性是否为自有属性
const obj = { a: 1 }
obj.hasOwnProperty('a') // true
obj.hasOwnProperty('toString') // false (继承自原型)obj.isPrototypeOf(v)
检查对象是否在 v 的原型链中
const proto = { a: 1 }
const child = Object.create(proto)
proto.isPrototypeOf(child) // true
Object.prototype.isPrototypeOf(child) // true
({}).isPrototypeOf(child) // falseobj.propertyIsEnumerable(v)
检查属性是否为可枚举的自有属性
const obj = { a: 1 }
Object.defineProperty(obj, 'b', { value: 2, enumerable: false })
obj.propertyIsEnumerable('a') // true
obj.propertyIsEnumerable('b') // falseobj.toString()
返回对象的字符串表示
({}).toString() // "[object Object]"
([1, 2]).toString() // "1,2"
(function(){}).toString() // "function (){}"
(new Date()).toString() // "Tue Mar 03 2026 ..."
// 判断类型(古老但有用)
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
// 自定义类的 toString
class Person {
constructor(name) { this.name = name }
toString() { return this.name }
}
new Person('John').toString() // "John"obj.valueOf()
返回对象的原始值
new Number(5).valueOf() // 5
new String('hi').valueOf() // "hi"
new Date().valueOf() // 1706745600000 (时间戳)obj.toLocaleString()
本地化字符串表示
// 默认返回 toString
({}).toLocaleString() // "[object Object]"
// Array 有自己的实现
[1, 2, 3].toLocaleString() // "1, 2, 3" (逗号可能因地区不同)
// Date 有自己的实现
new Date().toLocaleString() // "2026/3/3 下午4:30:00"4.2 遗留方法(proto 和其他)
obj.proto
访问器属性,获取/设置对象的原型
const proto = { method() {} }
const obj = {}
// 获取
obj.__proto__ === Object.prototype // true
// 设置(不推荐)
obj.__proto__ = proto
obj.method() // 现在可以调用了注意:__proto__ 是历史遗留属性,现代代码应使用:
Object.getPrototypeOf(obj)替代读取Object.setPrototypeOf(obj, proto)替代写入
obj.defineGetter(prop, getter)
定义 getter(遗留方法,不推荐)
obj.__defineGetter__('age', function() { return this._age })
// 现代替代
Object.defineProperty(obj, 'age', {
get() { return this._age }
})obj.defineSetter(prop, setter)
定义 setter(遗留方法,不推荐)
obj.lookupGetter(prop)
查找 getter(遗留方法,不推荐)
obj.lookupSetter(prop)
查找 setter(遗留方法,不推荐)
五、属性描述符详解
5.1 数据属性描述符
{
value: 'hello', // 属性值
writable: true, // 是否可修改
enumerable: true, // 是否可枚举
configurable: true // 是否可删除/重新配置
}5.2 访问器属性描述符
{
get: function() { return this._name },
set: function(v) { this._name = v },
enumerable: true,
configurable: true
}5.3 默认值对比
| 方式 | enumerable | configurable | writable | value |
|---|---|---|---|---|
{} 字面量 | true | true | true | 指定值 |
Object.defineProperty | false | false | false | undefined |
六、实战技巧
6.1 深拷贝
// 简单深拷贝
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj
if (Array.isArray(obj)) return obj.map(deepClone)
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k, deepClone(v)])
)
}
// 使用 structuredClone(现代API)
const clone = structuredClone(original) // 支持更多类型6.2 私有属性
class Counter {
#count = 0 // 私有字段
get count() {
return this.#count
}
increment() {
this.#count++
}
}6.3 不可变对象
const frozen = Object.freeze({
nested: Object.freeze({ value: 1 })
})
// 嵌套对象也被冻结6.4 属性代理
const handler = {
get(target, prop) {
console.log(`Getting ${prop}`)
return target[prop]
},
set(target, prop, value) {
console.log(`Setting ${prop} to ${value}`)
target[prop] = value
}
}
const proxy = new Proxy({}, handler)七、速查表
创建对象
| 方法 | 用途 |
|---|---|
{} | 字面量创建 |
Object.create(null) | 无原型对象 |
Object.create(proto) | 指定原型 |
new Object(value) | 强制转为对象 |
属性操作
| 方法 | 用途 |
|---|---|
Object.defineProperty | 定义单个属性 |
Object.defineProperties | 批量定义属性 |
Object.getOwnPropertyDescriptor | 获取属性描述符 |
Object.getOwnPropertyDescriptors | 获取所有描述符 |
查询
| 方法 | 用途 |
|---|---|
Object.keys | 可枚举属性名 |
Object.values | 可枚举属性值 |
Object.entries | 可枚举键值对 |
Object.getOwnPropertyNames | 所有属性名(含不可枚举) |
Object.getOwnPropertySymbols | 所有 Symbol 属性 |
Object.hasOwn | 自有属性检查 |
原型
| 方法 | 用途 |
|---|---|
Object.getPrototypeOf | 获取原型 |
Object.setPrototypeOf | 设置原型 |
obj.isPrototypeOf | 原型链检查 |
保护
| 方法 | 用途 |
|---|---|
Object.freeze | 冻结(不可修改) |
Object.seal | 密封(不可增删) |
Object.preventExtensions | 阻止扩展 |
Object.isFrozen | 检查冻结 |
Object.isSealed | 检查密封 |
Object.isExtensible | 检查可扩展 |
比较
| 方法 | 用途 |
|---|---|
Object.is | 精确相等 |
Object.isExtensible | 可扩展性 |
Object.isFrozen | 冻结状态 |
Object.isSealed | 密封状态 |
总结
Object 是 JavaScript 的核心:
- 创建:字面量、
Object.create、构造函数 - 属性:定义、查询、保护
- 原型:获取、设置、检查继承
- 保护:freeze、seal、preventExtensions
- 枚举:keys、values、entries
- 比较:Object.is