JavaScript中继承(二)-- 借用构造函数

时间:2022-03-16 19:29:33

 在具体介绍采用构造函数来实现继承之前,我们先回顾一下上一篇中将的原型继承存在的问题。这样才能够做到温故而知新!

原型继承方式存在着以下两个重要的问题:

①:超类中的实例属性,如果是引用类型的话,那么这一属性将随着子类原型对象的重写而变成所有子类共享的属性。任意一个子类实例对该属性的修改,都将在其余所有子类中感知到该修改。这一点并不是我们所期待看见的。

②:使用原型继承时,我们在创建子类型的实例时,不能够在不影响所有对象实例的情况下向超类的构造函数中传递参数,比如说:我们现在想对一个实例对象初始化时指定其name属性为张三,而另一个实例对象为李四,如果我们采用了原型继承的,这一要求是不可能实现的。


    借用构造函数(constructor stealing)也叫做伪造对象或是经典继承。该方式的基本思想是:在子类型构造函数的内部调用超类型的构造函数。之所能够这样做,我们还得复习一下JavaScript中的函数(这里不再赘述了,回头再将笔记上传)。JavaScript中函数只不过是在特定环境中执行代码的对象(换句话说就是JavaScript中任意函数都可以当做对象对待),因此我们可以通过使用apply()和call()方法也可以在新创建的对象上执行构造函数。

来一段实例代码效果最明显了:

function SuperType(){
this.colors = ["red", "blue", "green"]; // 父类对象中存在着引用类型,这里是一个数组类型
}

function SubType(){
SuperType.call(this); // 继承了SuperType,别忘记将this对象传递进去
}
window.onload = function(){
var instance_1 = new SubType();
instance_1.colors.push("black");
alert(instance_1.colors); // >> "red, blue, green, black"

var instance_2 = new SubType();
alert(instance_2.colors); // >> "red, blue, green"
}
如果将上面的代码改为原型继承方式,那么结果可能就不是我们想要的了:
function SuperType(){
this.colors = ["red", "blue", "green"]; // 父类对象中存在着引用类型,这里是一个数组类型
}

function SubType(){
//SuperType.call(this); // 继承了SuperType,别忘记将this对象传递进去
}

SubType.prototype = new SuperType(); // 该为原型继承,那么将会有引用类型值的问题出现!!!!

window.onload = function(){
var instance_1 = new SubType();
instance_1.colors.push("black");
alert(instance_1.colors); // >> "red, blue, green, black"

var instance_2 = new SubType();
alert(instance_2.colors); // >> "red, blue, green, black" 这里的输出结果并不是我们想要的
}

借用构造函数实现继承和原型继承的相比,有什么优点:

①:可以传递参数,实现个性化定制(初始化)对象实例:代码示例如下

function SuperType(name){
this.colors = ["red", "blue", "green"]; // 父类对象中存在着引用类型,这里是一个数组类型
this.name = name;
}

function SubType(name, age){
SuperType.call(this, name); // 继承了SuperType,别忘记将this对象传递进去
this.age = age;
}

//SubType.prototype = new SuperType(); // 该为原型继承,那么将会有引用类型值的问题出现!!!!

window.onload = function(){
var instance_1 = new SubType("大头", 22);
alert("name : " + instance_1.name + " age : " + instance_1.age);

var instance_2 = new SubType("shenlei", 23);
alert("name : " + instance_2.name + " age : " + instance_2.age);
}
对比下一原型继承的效果:

function SuperType(name){
this.colors = ["red", "blue", "green"]; // 父类对象中存在着引用类型,这里是一个数组类型
this.name = name;
}

function SubType(name, age){
//SuperType.call(this, name); // 继承了SuperType,别忘记将this对象传递进去
this.age = age;
}

SubType.prototype = new SuperType("Domee"); // 该为原型继承,那么将会有引用类型值的问题出现!!!!

window.onload = function(){
var instance_1 = new SubType("大头", 22);
alert("name : " + instance_1.name + " age : " + instance_1.age); // 输出来的name全是Domee

var instance_2 = new SubType("shenlei", 23);
alert("name : " + instance_2.name + " age : " + instance_2.age);
}

代码分析:借用构造函数实现继承的代码中,SuperType接受一个参数name,该参数将赋值给一个属性。在SubType构造函数内部调用SuperType构造函数时,实际上是为SubType的实例设置了name属性。需要注意一点的是: 为了确保SuperType构造函数不会重写子类型的属性,可以在调用超类型构造函数后,再添加应该在子类型中定义的属性。就如同Java中子类中调用父类的构造方法时,super关键字应该方法构造函数的第一句话一样。


借用构造函数实现继承存在的不足:

    在借用构造函数实现继承这一模式下,方法都得在构造函数中定义,因此就不存在函数的复用了。而且在这一模式下,父类在其原型中定义的方法,对子类而言是不可见的。


解决之道:

虽然借用构造函数实现继承解决了原型模式存在的问题,却屏蔽了原型模式的优点带来了新的问题。那么如何采用实现更优的继承呢,哈哈!下次笔记在说了······