本文详细解析JavaScript中基于原型链的继承实现方式。通过将子类的prototype属性设置为父类的实例,子类可自动继承父类的所有属性和方法。文章重点探讨了原型链中instanceof运算符的特殊行为,以及重写prototype导致的constructor指向Object而非子类的现象,并提供了相应的代码示例与测试说明。
原型链继承的基本实现
若采用原型方式重新定义前文示例中的类,其结构将调整为以下形式:
代码如下:
function ClassA() {
}
ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB() {
}
ClassB.prototype = new ClassA();原型方式的核心在于最后一行代码。此处将 ClassB 的 prototype 属性赋值为 ClassA 的实例。这一做法的精妙之处在于,无需逐个将 ClassA 的属性和方法添加到 ClassB 的 prototype 中,即可直接获取 ClassA 的所有属性和方法。将 ClassA 的实例赋予 prototype 属性,是实现这一目标的有效手段。
注意:在调用 ClassA 构造函数时,并未传递任何参数。这是原型链中的标准做法,务必确保构造函数不接收参数。
与对象冒充类似,子类的所有属性和方法必须在 prototype 属性被赋值之后进行定义。若在赋值前添加方法,这些方法将被销毁,因为 prototype 属性已被替换为新对象。因此,为 ClassB 类添加 name 属性和 sayName() 方法的代码如下:
代码如下:
function ClassB() {
}
ClassB.prototype = new ClassA();
ClassB.prototype.name = "";
ClassB.prototype.sayName = function () {
alert(this.name);
};原型链继承的测试示例
可通过运行以下示例来验证代码逻辑:
代码如下:
var objA = new ClassA();
var objB = new ClassB();
objA.color = "blue";
objB.color = "red";
objB.name = "John";
objA.sayColor();
objB.sayColor();
objB.sayName();instanceof运算符与constructor指向问题
此外,在原型链机制下,instanceof 运算符的行为也较为独特。对于 ClassB 的所有实例,instanceof 运算符对 ClassA 和 ClassB 均返回 true。例如:
代码如下:
var objB = new ClassB();
alert(objB instanceof ClassA); //输出 "true"
alert(objB instanceof ClassB); //输出 "true"在 ECMAScript 的弱类型环境中,这是一个非常有用的工具,但在使用对象冒充时无法通过该方法进行判断。然而,由于子类的原型被直接重新赋值,会出现以下情况:
代码如下:
console.log(objB.__proto__===objB.constructor.prototype) //false这是因为 ClassB 的原型链 prototype 属性被另一个类的对象所覆盖。从输出结果可以看出,objB.__proto__ 仍然指向 ClassB.prototype,而非 objB.constructor.prototype。这不难理解,因为给 Person.prototype 赋值的是一个对象直接量 new ClassA() 的实例,使用对象直接量方式定义的对象,其构造器(constructor)指向的是根构造器 Object。由于 Object.prototype 是一个空对象 {},它自然与 ClassB.prototype 不相等。