Skip to content

Object 新增方法

ES6 及后续版本为 Object 添加了许多实用的静态方法。

Object.assign()#

将源对象的可枚举属性复制到目标对象:

const target = { a: 1 }
const source1 = { b: 2 }
const source2 = { c: 3 }
const result = Object.assign(target, source1, source2)
console.log(result) // { a: 1, b: 2, c: 3 }
console.log(target) // { a: 1, b: 2, c: 3 }(target 被修改)
console.log(result === target) // true

基本特性#

// 同名属性后面覆盖前面
Object.assign({ a: 1 }, { a: 2 }, { a: 3 }) // { a: 3 }
// 只复制自有可枚举属性
const obj = Object.create({ inherited: true })
obj.own = true
Object.defineProperty(obj, 'nonEnum', {
value: 'hidden',
enumerable: false,
})
Object.assign({}, obj) // { own: true }
// 浅复制
const original = { nested: { value: 1 } }
const copy = Object.assign({}, original)
copy.nested.value = 2
console.log(original.nested.value) // 2(被影响了)

实际应用#

// 合并配置
function createServer(options) {
const defaults = { port: 3000, host: 'localhost' }
const config = Object.assign({}, defaults, options)
return config
}
// 克隆对象(浅复制)
const clone = Object.assign({}, original)
// 对象混入
function mixin(target, ...sources) {
return Object.assign(target, ...sources)
}

🔶 注意:更推荐使用扩展运算符:

Object.assign
const merged = Object.assign({}, obj1, obj2)
// 扩展运算符(更简洁,且不修改第一个对象)
const merged2 = { ...obj1, ...obj2 }

Object.is()#

判断两个值是否相同,解决了 === 的特殊情况:

// === 的问题
;+0 === -0 // true
NaN === NaN // false
// Object.is 修复了这些
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
// 其他情况与 === 相同
Object.is(1, 1) // true
Object.is('a', 'a') // true
Object.is({}, {}) // false(不同引用)

Object.keys() / values() / entries()#

获取对象的键、值、键值对数组:

const obj = { a: 1, b: 2, c: 3 }
Object.keys(obj) // ['a', 'b', 'c']
Object.values(obj) // [1, 2, 3]
Object.entries(obj) // [['a', 1], ['b', 2], ['c', 3]]

遍历对象#

const user = { name: '张三', age: 25, city: '北京' }
// 遍历键
Object.keys(user).forEach((key) => {
console.log(key)
})
// 遍历值
Object.values(user).forEach((value) => {
console.log(value)
})
// 遍历键值对
Object.entries(user).forEach(([key, value]) => {
console.log(`${key}: ${value}`)
})
// for...of + 解构
for (const [key, value] of Object.entries(user)) {
console.log(`${key}: ${value}`)
}

对象转 Map#

const obj = { a: 1, b: 2 }
const map = new Map(Object.entries(obj))
console.log(map) // Map { 'a' => 1, 'b' => 2 }

Object.fromEntries()#

ES2019 新增,entries 的逆操作:

// 从键值对数组创建对象
const entries = [
['a', 1],
['b', 2],
['c', 3],
]
Object.fromEntries(entries) // { a: 1, b: 2, c: 3 }
// Map 转对象
const map = new Map([
['name', '张三'],
['age', 25],
])
Object.fromEntries(map) // { name: '张三', age: 25 }

实际应用#

// 过滤对象属性
const obj = { a: 1, b: 2, c: 3, d: 4 }
const filtered = Object.fromEntries(
Object.entries(obj).filter(([key, value]) => value > 2)
)
// { c: 3, d: 4 }
// 转换对象值
const prices = { apple: 1.5, banana: 2.0, orange: 1.8 }
const discounted = Object.fromEntries(
Object.entries(prices).map(([fruit, price]) => [fruit, price * 0.9])
)
// { apple: 1.35, banana: 1.8, orange: 1.62 }
// URL 参数解析
const params = new URLSearchParams('name=张三&age=25')
Object.fromEntries(params) // { name: '张三', age: '25' }

Object.getOwnPropertyDescriptors()#

获取对象所有自有属性的描述符:

const obj = {
name: '张三',
get fullName() {
return this.name
},
}
Object.getOwnPropertyDescriptors(obj)
// {
// name: {
// value: '张三',
// writable: true,
// enumerable: true,
// configurable: true
// },
// fullName: {
// get: [Function: get fullName],
// set: undefined,
// enumerable: true,
// configurable: true
// }
// }

完整复制对象#

Object.assign 无法正确复制 getter/setter:

const source = {
get foo() {
return 'foo'
},
}
// assign 复制的是值,不是 getter
const copy1 = Object.assign({}, source)
Object.getOwnPropertyDescriptor(copy1, 'foo')
// { value: 'foo', writable: true, ... }
// 使用 getOwnPropertyDescriptors 保持 getter
const copy2 = Object.defineProperties(
{},
Object.getOwnPropertyDescriptors(source)
)
Object.getOwnPropertyDescriptor(copy2, 'foo')
// { get: [Function], set: undefined, ... }

Object.hasOwn()#

ES2022 新增,替代 hasOwnProperty

const obj = { name: '张三' }
// 传统方式
obj.hasOwnProperty('name') // true
Object.prototype.hasOwnProperty.call(obj, 'name') // true(更安全)
// ES2022
Object.hasOwn(obj, 'name') // true
Object.hasOwn(obj, 'toString') // false(继承属性)
// 处理特殊对象
const nullProto = Object.create(null)
nullProto.name = '张三'
// nullProto.hasOwnProperty('name'); // TypeError
Object.hasOwn(nullProto, 'name') // true ✅

Object.groupBy()#

ES2024 新增,按条件分组:

const people = [
{ name: '张三', age: 25 },
{ name: '李四', age: 30 },
{ name: '王五', age: 25 },
]
// 按年龄分组
Object.groupBy(people, (person) => person.age)
// {
// 25: [{ name: '张三', age: 25 }, { name: '王五', age: 25 }],
// 30: [{ name: '李四', age: 30 }]
// }
// 按条件分组
Object.groupBy(people, (person) => (person.age >= 30 ? 'senior' : 'junior'))
// {
// junior: [{ name: '张三', age: 25 }, { name: '王五', age: 25 }],
// senior: [{ name: '李四', age: 30 }]
// }

setPrototypeOf / getPrototypeOf#

操作对象原型:

const proto = {
greet() {
return 'Hello'
},
}
const obj = { name: '张三' }
// 设置原型
Object.setPrototypeOf(obj, proto)
obj.greet() // 'Hello'
// 获取原型
Object.getPrototypeOf(obj) === proto // true

🔶 注意:setPrototypeOf 性能较差,应避免频繁使用。

实战应用#

对象深度比较#

function deepEqual(obj1, obj2) {
if (Object.is(obj1, obj2)) return true
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return false
if (obj1 === null || obj2 === null) return false
const keys1 = Object.keys(obj1)
const keys2 = Object.keys(obj2)
if (keys1.length !== keys2.length) return false
return keys1.every(
(key) => Object.hasOwn(obj2, key) && deepEqual(obj1[key], obj2[key])
)
}

对象转换#

// 所有值转大写
function mapValues(obj, fn) {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [key, fn(value)])
)
}
mapValues({ a: 'hello', b: 'world' }, (v) => v.toUpperCase())
// { a: 'HELLO', b: 'WORLD' }
// 重命名键
function renameKeys(obj, keyMap) {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [keyMap[key] || key, value])
)
}
renameKeys({ name: '张三', age: 25 }, { name: 'username' })
// { username: '张三', age: 25 }

对象 pick / omit#

function pick(obj, keys) {
return Object.fromEntries(
keys.filter((key) => Object.hasOwn(obj, key)).map((key) => [key, obj[key]])
)
}
function omit(obj, keys) {
const keySet = new Set(keys)
return Object.fromEntries(
Object.entries(obj).filter(([key]) => !keySet.has(key))
)
}
const user = { id: 1, name: '张三', password: '123' }
pick(user, ['id', 'name']) // { id: 1, name: '张三' }
omit(user, ['password']) // { id: 1, name: '张三' }

方法汇总#

方法版本作用
Object.assign()ES6复制/合并对象
Object.is()ES6严格相等比较
Object.keys()ES5.1获取键数组
Object.values()ES2017获取值数组
Object.entries()ES2017获取键值对数组
Object.fromEntries()ES2019键值对数组转对象
Object.getOwnPropertyDescriptors()ES2017获取所有属性描述符
Object.hasOwn()ES2022检查自有属性
Object.groupBy()ES2024按条件分组