JavaScript浅拷贝本身不支持自定义规则,需手动实现可控浅拷贝函数,如用Object.keys()遍历并条件处理,或用getOwnPropertyDescriptors保留属性特性,亦可配置化封装,但本质仍是浅拷贝。

JavaScript 中的浅拷贝本身不支持自定义规则,它只是复制对象第一层属性的值(对引用类型只复制地址)。若需自定义拷贝逻辑(比如跳过某些字段、转换值、深层处理部分属性),必须绕过原生浅拷贝方法(如 Object.assign()、展开运算符 {...obj}),手动实现一个“可控的浅拷贝”函数。
用 Object.keys() + for...in 手动遍历 + 条件判断
这是最直接的方式:遍历源对象的自有可枚举属性,对每个 key 做个性化处理,再赋值到新对象中。
- 使用
Object.keys(obj)或for...in+hasOwnProperty获取键名 - 对每个 key 判断是否需要跳过、重命名、转换值或特殊处理
- 手动赋值,不依赖默认行为
示例:过滤掉以 _ 开头的私有字段,并将 name 转为大写:
function customShallowCopy(obj) {
const copy = {};
for (const key of Object.keys(obj)) {
if (key.startsWith('_')) continue; // 跳过私有字段
if (key === 'name') {
copy[key] = obj[key]?.toUpperCase(); // 自定义转换
} else {
copy[key] = obj[key]; // 默认浅拷贝
}
}
return copy;
}
<p>const user = { name: 'alice', _id: 123, age: 30 };
console.log(customShallowCopy(user)); // { name: 'ALICE', age: 30 }
用 Object.getOwnPropertyDescriptors() 保留属性特性
如果源对象包含 getter/setter、不可枚举属性或 writable/enumerable 等配置,普通遍历会丢失这些元信息。此时可用 Object.getOwnPropertyDescriptors() 获取完整描述符,再按需修改后用 Object.defineProperties() 创建新对象。
立即学习“Java免费学习笔记(深入)”;
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 适合需要控制属性行为(如只拷贝 getter、禁用某个 setter)的场景
- 对每个 descriptor 可修改
value、跳过get、或替换为新逻辑 - 注意:该方法不处理原型链上的属性,仅限自有属性
示例:拷贝时把所有 getter 的返回值提前计算并转为普通 value:
function customShallowCopyWithDescriptors(obj) {
const descriptors = {};
for (const key of Object.keys(obj)) {
const desc = Object.getOwnPropertyDescriptor(obj, key);
if (desc && 'get' in desc && typeof desc.get === 'function') {
descriptors[key] = {
value: desc.get.call(obj), // 立即执行 getter
writable: true,
enumerable: desc.enumerable,
configurable: desc.configurable
};
} else {
descriptors[key] = { ...desc }; // 原样保留
}
}
return Object.defineProperties({}, descriptors);
}
结合 Proxy 实现动态拷贝拦截(适合调试或高级封装)
Proxy 不用于真正生成拷贝对象,但可在拷贝过程中监听访问、修改行为,实现运行时规则注入。实际项目中更常见的是封装一个可配置的拷贝工具函数,而非每次用 Proxy。
- 定义配置项:如
exclude、transform、rename等 - 函数内部仍基于手动遍历,但逻辑由配置驱动,复用性强
- 避免过度设计:简单需求无需 Proxy;复杂规则建议用配置化函数
轻量配置示例:
function shallowCopyWithRules(obj, rules = {}) {
const { exclude = [], transform = {}, rename = {} } = rules;
const copy = {};
for (const key of Object.keys(obj)) {
if (exclude.includes(key)) continue;
const newKey = rename[key] || key;
const value = key in transform
? transform[key](obj[key], obj)
: obj[key];
copy[newKey] = value;
}
return copy;
}
<p>shallowCopyWithRules({ a: 1, b: 2, c: 3 }, {
exclude: ['c'],
transform: { a: x => x * 2 },
rename: { b: 'B' }
}); // { a: 2, B: 2 }
注意边界:浅拷贝 ≠ 深拷贝,自定义也不改变本质
无论怎么定制,只要没递归处理嵌套对象,就仍是浅拷贝——嵌套对象、数组、函数等引用类型依然共享内存。
- 如果某字段是对象,且你希望它也被“按规则处理”,那就要在自定义逻辑里加递归分支(此时已接近深拷贝)
- 纯浅拷贝 + 自定义,只应作用于顶层属性;深层逻辑属于深拷贝范畴,需额外设计
- 别混淆概念:自定义规则 ≠ 改变拷贝深度,只是控制“拷什么、怎么拷”
不复杂但容易忽略。

















