JavaScript 对象如何遍历与复制?Object.keys、assign 与 structuredClone 用法
2026/9/9 22:36:09 网站建设 项目流程

JavaScript 对象如何遍历与复制?Object.keys、assign 与 structuredClone 用法

【免费下载链接】33-js-concepts📜 33 JavaScript concepts every developer should know.项目地址: https://gitcode.com/GitHub_Trending/33/33-js-concepts

当你拿到一个 JavaScript 对象,需要枚举它的属性做日志、汇总或转换,或者需要拿一份可独立修改的副本(浅拷贝或深拷贝)而不影响原对象时,Object构造器上的静态方法就是 33-js-concepts 项目文档给出的工具路径:用Object.keys()Object.values()Object.entries()遍历,用Object.assign()做浅拷贝/合并,用structuredClone()做深拷贝。这篇项目文档同时附带一组 vitest 测试(object-methods.test.js),可以实际跑起来核对每一步的行为是否符合文档描述。

准备条件

  • 一个支持这些方法的 JS 运行环境(浏览器或 Node.js)。其中structuredClone()是 2022 年才加入浏览器和 Node.js 的;文档指出它取代了此前JSON.parse(JSON.stringify(obj))的变通写法——后者对DateMapSet和循环引用都会失效。
  • 若要在本仓库中跑验证测试,需要 Node.js 环境,依赖已在package.json的 devDependencies 中声明(vitest 4.x)。

第一步:用 keys / values / entries 遍历对象

这三个方法把对象转成数组,供循环或转换使用。以文档中的示例对象为例:

const user = { name: 'Alice', age: 30, city: 'NYC' } const keys = Object.keys(user) console.log(keys) // ['name', 'age', 'city'] const values = Object.values(user) console.log(values) // ['Alice', 30, 'NYC'] const entries = Object.entries(user) console.log(entries) // [['name', 'Alice'], ['age', 30], ['city', 'NYC']]

选哪个取决于你要什么:

方法返回适用时机
Object.keys(obj)['key1', 'key2', ...]只需要属性名
Object.values(obj)[value1, value2, ...]只需要值
Object.entries(obj)[['key1', value1], ...]键和值都要

需要强调的边界:这三个方法只返回自身(own)可枚举属性,会跳过原型链上的继承属性和不可枚举属性。如果需要包括不可枚举属性,用Object.getOwnPropertyNames()

const arr = [1, 2, 3] Object.keys(arr) // ['0', '1', '2'] Object.getOwnPropertyNames(arr) // ['0', '1', '2', 'length']

length是数组的不可枚举属性,所以Object.keys()看不到它。

可选分支:用 entries + fromEntries 转换键

Object.fromEntries()Object.entries()的逆操作,两者配合数组方法可以整体变换对象。文档给出的模式是把键全部转大写:

const upperCased = Object.fromEntries( Object.entries(user).map(([key, value]) => [key.toUpperCase(), value]) ) console.log(upperCased) // { NAME: 'Alice', AGE: 30, CITY: 'NYC' }

同样地,用filter可以按值类型筛掉属性:

const product = { name: 'Laptop', price: 999, inStock: true, sku: 'LP001' } const stringsOnly = Object.fromEntries( Object.entries(product).filter(([key, value]) => typeof value === 'string') ) console.log(stringsOnly) // { name: 'Laptop', sku: 'LP001' }

第二步:用 Object.assign 做浅拷贝与合并

JavaScript 对象按引用赋值,Object.assign()把所有可枚举自身属性从源对象复制到目标对象:

const target = { a: 1 } const source = { b: 2 } Object.assign(target, source) console.log(target) // { a: 1, b: 2 }

两种常见用法:

// 克隆:以空对象为目标 const original = { name: 'Alice', age: 30 } const clone = Object.assign({}, original) clone.name = 'Bob' console.log(original.name) // 'Alice' — 原对象不受影响 // 合并多个对象:后面的源覆盖前面 const defaults = { theme: 'light', fontSize: 14 } const userPrefs = { theme: 'dark' } const settings = Object.assign({}, defaults, userPrefs) console.log(settings) // { theme: 'dark', fontSize: 14 }

关键限制:这是浅拷贝。嵌套对象仍是共享引用,文档中的示例直接演示了这个陷阱:

const original = { name: 'Alice', address: { city: 'NYC' } } const clone = Object.assign({}, original) clone.address.city = 'LA' console.log(original.address.city) // 'LA' — 两边都变了!

顶层属性独立了(改clone.name不影响original.name),但address这个嵌套对象是同一个引用。如果嵌套属性必须独立,用下一步的structuredClone()

第三步:用 structuredClone 做深拷贝

structuredClone()创建真正的深拷贝,嵌套对象一并独立:

const original = { name: 'Alice', address: { city: 'NYC' } } const clone = structuredClone(original) clone.address.city = 'LA' console.log(original.address.city) // 'NYC' — 原对象保持不变

除了嵌套对象,它还能正确处理:

  • 循环引用;
  • 大多数内置类型,如DateMapSetArrayBuffer
const data = { date: new Date('2024-01-01'), items: new Set([1, 2, 3]) } const clone = structuredClone(data) console.log(clone.date instanceof Date) // true console.log(clone.items instanceof Set) // true

它不能克隆的东西(文档明确列出):

  • 函数;
  • DOM 节点;
  • 属性描述符(getter/setter 会变成普通值);
  • 原型链(克隆出来的是普通对象)。

包含函数时直接抛错:

const obj = { greet: () => 'Hello' // Function } structuredClone(obj) // Throws: DataCloneError

浅拷贝还是深拷贝:怎么选

文档给出的对照表:

方法深度速度适用时机
Object.assign()合并对象、无嵌套对象
Spread{...obj}快速克隆、无嵌套对象
structuredClone()嵌套对象必须相互独立

注意{...obj}Object.assign({}, obj)等价,同样是浅拷贝——不要因为写法不同就以为它解决了嵌套共享的问题。

跑仓库测试验证行为

上面的示例值都可以在本仓库的测试中逐一核对。测试位于 tests/beyond/objects-properties/object-methods/object-methods.test.js,测试环境为 Node(见 vitest.config.js)。在仓库根目录执行:

npm install npm test

npm test对应 package.json 中的"test": "vitest run",会跑全部测试。如果只想核对对象方法这一部分,把 vitest 指向该测试文件:

npx vitest run tests/beyond/objects-properties/object-methods/object-methods.test.js

测试断言与文档示例一一对应,几个关键判断点:

  • Object.keys({ name: 'Alice', age: 30, city: 'NYC' })返回['name', 'age', 'city']
  • Object.values(scores).reduce(...){ math: 95, science: 88, history: 92 }求和得到275
  • 浅拷贝示例中clone.address.city = 'LA'之后,original.address.city也是'LA'(嵌套共享的实证);
  • structuredClone的深拷贝示例中,修改clone.address.cityoriginal.address.city仍为'NYC'
  • structuredClone克隆含函数的对象会抛错(测试用expect(() => structuredClone(obj)).toThrow()断言);
  • 不可枚举属性验证:用Object.definePropertyenumerable设为false后,Object.keys/Object.values/Object.entries都只返回可枚举属性。

测试全部通过,即说明当前运行环境的行为与文档一致。

已知的边界与限制

  • Object.keys()/values()/entries()跳过继承属性和不可枚举属性;要看全用Object.getOwnPropertyNames(),看 Symbol 键用Object.getOwnPropertySymbols()
  • Object.assign()只拷贝顶层;嵌套对象仍是共享引用。
  • structuredClone()遇到函数会抛DataCloneError,不能用于含函数、DOM 节点的对象;克隆结果不带原型链,getter/setter 会退化为普通值。
  • structuredClone()要求运行环境在 2022 年之后(浏览器和 Node.js 均已加入);更老的环境中文档指出需要回退到JSON.parse(JSON.stringify(obj))之类的变通写法,但要清楚它无法处理DateMapSet和循环引用。

想继续深入属性枚举、描述符等底层机制,可以看仓库中同系列的 property-descriptors.mdx;想理解原型与继承,看 object-creation-prototypes.mdx。

【免费下载链接】33-js-concepts📜 33 JavaScript concepts every developer should know.项目地址: https://gitcode.com/GitHub_Trending/33/33-js-concepts

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询