You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

构造函数继承常见问题答疑:规范写法与避坑指南

构造函数继承常见问题解答

1. 判断对象是否为Parent实例的几种方式是否等价?

首先指出代码中第四个判断是笔误,Object.getPrototypeOf(obj) === Object.getPrototypeOf(obj) 恒为true,和判断是否为Parent实例无关,推测你想写的是 Object.getPrototypeOf(obj) === Parent.prototype。下面分析前三种(修正后的第四种)的差异:

  • obj.constructor === Parent:默认情况下结果为true,但不可靠。因为constructor属性挂载在Parent.prototype上,一旦修改Parent.prototype(比如重新赋值对象),或手动修改obj原型链上的constructor,这个判断就会失效。
  • obj.__proto__ === Parent.prototype:结果和修正后的第四种一致,但__proto__是非标准属性(仅部分浏览器实现),不推荐在生产代码中直接使用。它直接对比实例的原型对象和父类的原型对象,默认情况下相等,但若原型被修改则结果改变。
  • obj instanceof Parent:这是最可靠的判断方式之一。它检查obj的整条原型链上是否存在Parent.prototype,只要原型链上有匹配就返回true,不受constructor属性修改的影响(除非父类的prototype被完全替换)。
  • Object.getPrototypeOf(obj) === Parent.prototype:这是__proto__对比的标准替代写法,Object.getPrototypeOf是ES5规范方法,兼容性更好,推荐使用。

总结:默认场景下前三种(含修正后的第四种)结果一致,但可靠性和规范度不同。优先使用instanceof或Object.getPrototypeOf的对比方式,避免依赖constructor或__proto__。

2. 两段示例代码存在差异吗?

两段代码的输出结果虽然一致,但内部实现机制、规范性有明显差异:

第一段代码(this.__proto__ = new Shape())

function Shape() {
    this.checkedPoints = []
}
function Rectangle(a){
    this.__proto__ = new Shape()
    this.a = a; 
}

let m = new Rectangle("first")
let n = new Rectangle("second")
m.checkedPoints.push(1)
n.checkedPoints.push(2);

console.log(m.checkedPoints) // Array [ 1 ]
console.log(n.checkedPoints) // Array [ 2 ]
  • 机制:每个Rectangle实例的__proto__指向一个独立的Shape实例,checkedPoints是挂载在实例的原型(Shape实例)上的属性。因为每个Rectangle实例的原型都是新的Shape实例,所以各自的checkedPoints互不干扰。
  • 问题:使用非标准的__proto__属性,兼容性和规范性差;给每个实例单独设置原型会带来性能损耗;若Shape有原型方法,虽然能继承,但这种写法不是构造函数继承的标准实现。

第二段代码(Shape.apply(this))

function Shape() {
    this.checkedPoints = [];
}

function Rectangle(a) {
    Shape.apply(this);
    this.a = a;
}

let m = new Rectangle("first");
let n = new Rectangle("second");
m.checkedPoints.push(1);
n.checkedPoints.push(2);

console.log(m.checkedPoints); // Array [ 1 ]
console.log(n.checkedPoints); // Array [ 2 ]
  • 机制:这是**盗用构造函数(构造函数继承)**的标准写法。通过Shape.apply(this),将Shape构造函数内的this指向当前Rectangle实例,直接在Rectangle实例上创建checkedPoints属性,每个实例的checkedPoints都是独立的。
  • 优势:使用标准API,兼容性好;属性直接挂载在子类实例上,避免原型共享引用类型的问题;如果需要给父类构造函数传参,也能通过apply/call传递。

差异总结:

  1. 属性位置:第一段的checkedPoints在实例的原型上,第二段在实例自身。
  2. 继承方式:第一段是原型链继承的变体,第二段是标准构造函数继承。
  3. 规范性:第一段不推荐,第二段是构造函数继承的推荐写法。
  4. 扩展性:第一段能继承父类原型上的方法,第二段不能(若要继承原型方法,需结合原型链继承)。

3. 为何m是Shape的实例却无法访问Shape的属性?

function Shape() {
    this.checkedPoints = [];
}

function Rectangle(a) {
    this.a = a;
    Object.setPrototypeOf(this.__proto__, Shape.prototype)
}

let m = new Rectangle("first");
console.log(m instanceof Shape, m instanceof Rectangle) // true true
console.log(m.a) //first
m.checkedPoints.push(1) // m.checkedPoints is undefined

原因分两点:

  1. instanceof的判断逻辑:instanceof检查对象的原型链是否包含目标构造函数的prototype。代码中Object.setPrototypeOf(this.__proto__, Shape.prototype)相当于把Rectangle.prototype的原型设为Shape.prototype,所以m的原型链是:m -> Rectangle.prototype -> Shape.prototype -> Object.prototype,因此m instanceof Shape返回true。
  2. checkedPoints的挂载逻辑:Shape构造函数中的this.checkedPoints = []是给Shape的实例添加属性,而非挂载到Shape.prototype上。而你的Rectangle构造函数中没有调用Shape构造函数(比如Shape.call(this)),所以m实例上不会创建checkedPoints属性;同时Shape.prototype上也没有这个属性,自然访问不到。

修正写法:
如果要同时继承Shape的实例属性和原型方法,需要结合构造函数继承和原型链继承:

function Shape() {
    this.checkedPoints = [];
}

function Rectangle(a) {
    Shape.call(this); // 调用父类构造函数,给子类实例添加属性
    this.a = a;
}
// 设置原型链,继承父类原型方法
Object.setPrototypeOf(Rectangle.prototype, Shape.prototype);

let m = new Rectangle("first");
m.checkedPoints.push(1);
console.log(m.checkedPoints); // [1]
console.log(m instanceof Shape); // true

内容的提问来源于stack exchange,提问作者Boris Borais

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 05:47:50