Object 总结

24 天前
5

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.prototype

Object.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                  // true

3.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)  // TypeError

Object.seal(obj)

密封对象:不可添加/删除属性,但可修改现有属性

const obj = { a: 1 }
Object.seal(obj)

obj.a = 2        // 可以
obj.b = 3        // 不行
delete obj.a     // 不行

Object.isSealed(obj)  // true

Object.preventExtensions(obj)

阻止添加新属性(可删除/修改现有属性)

const obj = { a: 1 }
Object.preventExtensions(obj)

obj.b = 2        // 不行
obj.a = 2        // 可以
delete obj.a     // 可以

Object.isExtensible(obj)  // false

Object.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)  // false

3.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)      // false

obj.propertyIsEnumerable(v)

检查属性是否为可枚举的自有属性

const obj = { a: 1 }
Object.defineProperty(obj, 'b', { value: 2, enumerable: false })

obj.propertyIsEnumerable('a')  // true
obj.propertyIsEnumerable('b')  // false

obj.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 默认值对比

方式enumerableconfigurablewritablevalue
{} 字面量truetruetrue指定值
Object.definePropertyfalsefalsefalseundefined

六、实战技巧

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 的核心:

  1. 创建:字面量、Object.create、构造函数
  2. 属性:定义、查询、保护
  3. 原型:获取、设置、检查继承
  4. 保护:freeze、seal、preventExtensions
  5. 枚举:keys、values、entries
  6. 比较:Object.is

参考:ECMA-262 Object 规范

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...