JavaScript中实现部分属性深拷贝需先筛选再深拷贝:方法一用解构提取后深拷贝;方法二用路径白名单递归遍历;方法三用Lodash的pick与cloneDeep组合;禁用JSON方案,注意循环引用和性能。

JavaScript 中实现“仅拷贝对象部分属性的深拷贝”,本质是两步:先筛选出目标属性,再对这些属性值做深拷贝。不能直接用 JSON.parse(JSON.stringify()),因为它会忽略函数、undefined、Symbol、循环引用等,且无法按需选择字段。
方法一:手动提取 + 深拷贝工具(推荐)
先用解构或 Object.fromEntries() 提取需要的属性,再用可靠的深拷贝函数处理结果对象。
- 用解构快速筛选(适合已知键名):
<!-- 示例:只拷贝 name、address.city、tags -->
const source = {<br>
name: "Alice",<br>
age: 30,<br>
address: { city: "Beijing", zip: "100000" },<br>
tags: ["user", "active"],<br>
meta: { createdAt: new Date() }<br>
};
const partial = {<br>
name: source.name,<br>
city: source.address?.city,<br>
tags: Array.isArray(source.tags) ? source.tags.slice() : []<br>
};再对
partial 做深拷贝(如用 Lodash 的 _.cloneDeep(partial),或自定义递归函数)。
方法二:用结构化深拷贝 + 属性白名单(更健壮)
写一个支持“路径白名单”的深拷贝函数,只遍历并复制指定路径上的值(如 ["name", "address.city", "tags"]),跳过其他所有分支。
- 核心逻辑:递归时检查当前路径是否在白名单中或其前缀在白名单中
- 遇到数组/对象继续递归;匹配路径则深拷贝该值;不匹配且无子路径匹配,则跳过
- 可处理嵌套、数组索引(如
"items.0.name")、避免冗余遍历
示例片段(简化版):
立即学习“Java免费学习笔记(深入)”;
function deepPick(obj, paths) {<br>
const pathSet = new Set(paths);<br>
function walk(val, path = "") {<br>
if (val === null || typeof val !== "object") return val;<br>
if (Array.isArray(val)) {<br>
return val.map((item, i) => walk(item, `${path}${path ? "." : ""}${i}`));<br>
}<br>
// 对象<br>
const result = {};<br>
for (const key in val) {<br>
const currPath = path ? `${path}.${key}` : key;<br>
if (pathSet.has(currPath) || paths.some(p => p.startsWith(currPath + "."))) {<br>
result[key] = walk(val[key], currPath);<br>
}<br>
}<br>
return result;<br>
}<br>
return walk(obj);<br>
}
const copied = deepPick(source, ["name", "address.city", "tags"]);<br>
// → { name: "Alice", address: { city: "Beijing" }, tags: ["user", "active"] }
方法三:借助 Lodash 的 _.pick() + _.cloneDeep()
最简洁实用的工程方案:
-
_.pick(obj, ["a", "b.c", "d"])只能处理一级键(不支持点号路径) - 但搭配
_.set()和_.get()可构造路径提取逻辑 - 更推荐:用
_.cloneDeepWith()自定义克隆行为,跳过不需要的键
例如:
const picked = _.cloneDeepWith(source, (value, key, obj, stack) => {<br>
if (stack.size === 0) { // 根对象层<br>
return _.pick(value, ["name", "address", "tags"]);<br>
} else if (key === "address") {<br>
return _.pick(value, ["city"]);<br>
}<br>
});
注意事项
不要用 JSON 方法处理部分拷贝:它必须序列化整个对象,无法中途跳过字段,且丢失类型。
注意循环引用:自定义深拷贝需用 WeakMap 缓存已访问对象,否则栈溢出。
区分浅 vs 深需求:如果只取顶层几个属性且确认它们不包含引用类型,用解构 + 手动深拷贝子值即可,不必全量递归。
性能考虑:白名单路径越精确,遍历越少;避免对大对象做全量深拷贝后再删字段。


















