借用构造函数(Parent.call(this, ...))用于解决子实例间引用类型属性共享问题,而非防御原型污染;它通过在子构造函数中重放父类初始化逻辑,使每个实例拥有独立属性副本,前提是父类属性必须定义在this上且call调用位置正确、参数完整。

借用构造函数(即在子类中用 Parent.call(this, ...))本身不是用来“防止原型引用属性污染”的,而是为了解决构造函数继承中引用类型属性被共享的问题。它不作用于原型链,也不直接防御原型污染(Prototype Pollution),但能有效避免子实例间因共用父类原型上的引用属性而产生的意外修改。
为什么原型上的引用属性会“污染”其他实例?
当使用 Child.prototype = new Parent() 这类原型链继承时,若 Parent 构造函数未在 this 上初始化引用类型(如数组、对象),而是直接挂在 Parent.prototype 上,所有子实例都会共享该引用:
-
Parent.prototype.hobbies = ['reading']→ 所有new Child()实例的.hobbies都指向同一数组 - 一个实例执行
child1.hobbies.push('coding'),child2.hobbies也会变 - 这不是安全漏洞,而是设计误用导致的数据耦合
call 借用构造函数如何切断共享?
在子构造函数内部调用 Parent.call(this, ...),是把父类初始化逻辑“重放”到当前子实例上,让每个实例拥有独立的属性副本:
- 父类中写成
this.hobbies = ['reading'](而非挂原型) - 子类中写
Parent.call(this, name, age)→ 每次新建子实例,都执行一次this.hobbies = [...] - 结果:每个实例的
hobbies是独立数组,互不影响
关键注意事项
这种做法要真正生效,必须满足三个条件:
- 父类初始化逻辑必须写在构造函数体内(
this.xxx = ...),不能写在prototype上 -
Parent.call(this, ...)必须放在子构造函数最开始位置,确保属性尽早挂载 - 参数需完整传递,否则父类依赖的初始化值缺失,可能导致
undefined或逻辑错误
它和原型污染(Prototype Pollution)无关
需要明确区分:引用属性共享 是继承模式不当引起的运行时数据干扰;原型污染 是攻击者通过 __proto__ 等键名篡改 Object.prototype 导致的全局性安全漏洞。前者靠正确使用 call + 构造函数初始化来规避;后者靠 Object.create(null)、输入清洗、冻结原型等手段防御。

















