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 = trueObject.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 = 2console.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)}🔶 注意:更推荐使用扩展运算符:
const merged = Object.assign({}, obj1, obj2)
// 扩展运算符(更简洁,且不修改第一个对象)const merged2 = { ...obj1, ...obj2 }Object.is()#
判断两个值是否相同,解决了 === 的特殊情况:
// === 的问题;+0 === -0 // trueNaN === NaN // false
// Object.is 修复了这些Object.is(+0, -0) // falseObject.is(NaN, NaN) // true
// 其他情况与 === 相同Object.is(1, 1) // trueObject.is('a', 'a') // trueObject.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 复制的是值,不是 getterconst copy1 = Object.assign({}, source)Object.getOwnPropertyDescriptor(copy1, 'foo')// { value: 'foo', writable: true, ... }
// 使用 getOwnPropertyDescriptors 保持 getterconst copy2 = Object.defineProperties( {}, Object.getOwnPropertyDescriptors(source))Object.getOwnPropertyDescriptor(copy2, 'foo')// { get: [Function], set: undefined, ... }Object.hasOwn()#
ES2022 新增,替代 hasOwnProperty:
const obj = { name: '张三' }
// 传统方式obj.hasOwnProperty('name') // trueObject.prototype.hasOwnProperty.call(obj, 'name') // true(更安全)
// ES2022Object.hasOwn(obj, 'name') // trueObject.hasOwn(obj, 'toString') // false(继承属性)
// 处理特殊对象const nullProto = Object.create(null)nullProto.name = '张三'// nullProto.hasOwnProperty('name'); // TypeErrorObject.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 | 按条件分组 |