JavaScript继承最佳实践

JavaScript主要通过原型链实现继承。原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问超类型的所有属性和方法。

原型链的问题是对象实例共享所有继承的属性和方法,因此不适宜单独使用。解决这个问题是借用构造函数,即在子类型构造函数内部调用超类型构造函数(使用callapply),这样就做到每个实例都具有自己的属性,同时还能保证只使用构造函数模式来定义类型。

/**
 * 分三步来实现继承
 * 1. 创建超类型原型的一个副本
 * 2. 为创建的副本添加constructor属性,从而弥补因重写原型而失去默认的constructor属性
 * 3. 将新创建的对象(即副本)赋值给子类型原型
 * @param subClass 子类型构造函数
 * @param superClass 父类型构造函数
 */
function inheritPrototype(subClass, superClass) {
    var prototype = Object(superClass.prototype);   //创建对象
    prototype.constructor = subClass;   //增强对象
    subClass.prototype = prototype;     //指定对象
}

function superClass(name, age) {
    this.name = name;
    this.age = age;
    this.colors = ['red', 'blue', 'green'];
}

superClass.prototype.sayName = function () {
    return this.name;
}

function subClass(name, age) {
    superClass.call(this, name);
    this.age = age;
}

inheritPrototype(subClass, superClass);

/**
 * 为什么不使用此种方式?
 * 这种调用方式会导致调用两次超类型构造函数。
 * 第一调用为如下new superClass()的时候,会将superClass中的全部实例属性给subClass.prototype,也就是说会存在于subClass的原型中。
 * 第二次调用是在subClass的构造函数中,会将superClass的实例再次创建到subClass的构造函数中,也就屏蔽了原型中的两个同名属性name和age。
 * 所以如下的方式会导致在subClass原型上创建不必要的,多余的属性。
 */
//subClass.prototype = new superClass();

subClass.prototype.sayAge = function () {
    console.log(this.age)
}

var instance = new subClass('Zhangsan', 15);
instance.sayName();
instance.colors.push('yellow');
console.log(instance.colors);   //["red", "blue", "green", "yellow"]
var instance2 = new subClass('Lisi', 20);
instance2.colors.push('gray');
console.log(instance2.colors)   //["red", "blue", "green", "gray"]

该示例高效体现在只调用了一次superClass构造函数,并且因此避免了在subClass.prototype上面创建不必要的、多余的属性;与此同时,原型链还能保持不变,因此还能够正常使用instanceofisPrototypeOf()

如果您觉得本文对您有用,欢迎捐赠或留言~
微信支付
支付宝

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注