object.assign仅复制自有可枚举属性,不处理原型属性;需主动干预原型链行为,如用object.create(null)避免污染、提取原型默认值再合并、过滤危险键、或用object.defineproperties控制访问器。

Object.assign 本身不处理原型属性,它只复制对象自身的可枚举属性。所谓“原型属性优先级控制”,不是 Object.assign 的原生能力,而是需要你在调用前、后主动干预原型链行为——核心在于:**明确区分“自有属性”和“原型继承属性”,并按需决定谁覆盖谁、谁保留谁**。
明确目标对象的原型归属,避免意外污染
合并前先确保目标对象原型干净。直接用 {} 或 Object.create(null) 创建目标,能彻底排除 __proto__、constructor 等危险键被误赋值的风险:
-
Object.assign(Object.create(null), src1, src2)→ 无原型,绝对安全,适合配置字面量场景 -
Object.assign(Object.setPrototypeOf({}, MyProto), src1)→ 显式挂载指定原型,保留你认可的默认行为 - 避免
Object.assign(target, userInput),尤其当userInput来自 JSON 解析或表单时,可能含"__proto__": {...}导致原型链污染
手动注入“原型优先”或“实例优先”的合并逻辑
Object.assign 不读取原型属性,但你可以先提取原型上的默认值,再与实例属性合并。例如实现“原型属性为默认值,实例属性可覆盖”:
- 获取原型自有属性:
Object.getOwnPropertyNames(Object.getPrototypeOf(obj)) - 筛选出你想作为默认值的键(如
['timeout', 'retries']),构建成默认配置对象 - 再用
Object.assign(defaults, instanceConfig)完成“原型优先”的合并 - 反向操作(实例优先)则直接用
Object.assign(instance, Object.getPrototypeOf(instance)),但注意这会把原型属性“降级”为实例属性
过滤并拦截危险原型键,加固合并过程
即使使用 Object.assign,若源对象含 __proto__、constructor 或不可枚举属性,仍可能引发问题。建议封装一层安全合并函数:
- 遍历每个源对象的
Object.keys()(仅自有可枚举)+Object.getOwnPropertyNames()(含不可枚举),但显式跳过__proto__、constructor、prototype - 对每个键,检查
source.propertyIsEnumerable(key)和!Object.prototype.hasOwnProperty.call(source, key)判断是否来自原型 - 根据策略决定是否跳过、警告或重命名该键(如转为
_proto_config)
结合 Object.getOwnPropertyDescriptors 实现访问器属性控制
Object.assign 对 get/set 属性只调用 getter 取值、不保留访问器本身。如需保留或控制访问器优先级,改用 Object.defineProperties:
- 用
Object.getOwnPropertyDescriptors(source)获取完整描述符(含get、set、enumerable等) - 手动判断哪些描述符应来自原型、哪些来自实例,按需合并描述符对象
- 最后用
Object.defineProperties(target, mergedDescriptors)应用,真正实现“访问器级优先级控制”










