谈到继承,javascript在ES6出现之前,是不提供原生的继承机制的,这里我们将通过几种方法,来实现功能上的继承。
1.基于原型链的继承
function SuperType(){
this.colors=["red", "blue", "green"]
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
function SubType(){
}
SubType.prototype = new SuperType();
SubType.prototype.getSuperValue = function(){
return this.subproperty;
}
SubType.prototype.getSuperValue = function(){
return false;
}
var instance1 = new SubType();
instance1.colors.push("black");
var instance2 = new SubType();
console.info(instance2.colors)
result: [ 'red', 'blue', 'green', 'black' ]
console.info(instance1.hasOwnProperty("colors"), "colors" in instance1)
result: false true //说明colors这个属性不是子类的实例属性,而是原型属性
着重看第9行,子类的原型指向父类的实例,同样也就继承了父类实例后的所有属性,追着父类实例的原型链走,同样也继承到了父类的方法。
缺点:虽然得到了父类的属性和方法,但是都是在原型上的,当某一个实例对其属性进行更改时,会直接反应到原型上,如果这时实例化另外一个实例的话,就将接受这种更改,这往往是不能接受的,这在倒数。
2.借用构造函数来实现继承
function SuperType(){
this.colors=["red", "blue", "green"]
}
function SubType(){
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
console.info(instance1.colors) //red, blue, green, black
var instance2 = new SubType();
console.info(instance2.colors) //red, blue, green
console.info(instance1.hasOwnProperty("colors"), "colors" in instance1)
result: true true
着重看第五行,用了构造函数的call方法, call方法作用在与改变函数执行的作用域,第五行传入的this.也就是子类运行时的作用域,我们把子类作用域传入父类作用域,导致父类定义的那些属性会直接挂载到子类的作用域里,这个时候我们对子类进行实例化后得到的属性为实例属性。这个时候对各种对属性进行操作就不会影响到其他实例了。
缺点:如果方法也按照借用构造函数进行继承的话,那么方法他在每一个实例都会有一个拷贝,显然如果每个实例方法都是一样的话,会造成大量资源的浪费。
3.组合继承
组合继承: 我们把想要继承的属性通过借用构造函数的方法,把方法通过原型链进行继承。
function SuperType(name){
this.name = name;
this.colors=["red", "blue", "green"]
}
SuperType.prototype.sayName = function(){
console.info(this.name);
}
//借用构造函数完成属性的继承
function SubType(name, age){
SuperType.call(this, name); //添加公共属性
this.age = age; //添加自有属性
}
//借用原型链实现方法的继承
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
console.info(this.age);
}
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.info(instance1.colors) //red, blue, green, black
instance1.sayName(); //"Nicholas"
instance1.sayAge() //29
var instance2 = new SubType("Greg", 27);
console.info(instance2.colors) //red, blue, green
instance2.sayName() //"Greg"
instance2.sayAge() //27
但是细心的你会发现我们在借用原型链实现方法的继承时,不仅把父类的方法挂载到原型上,父类的属性也挂载到了原型上,但是我们也借用构造函数实现实例属性的继承,而原则上是不需要挂载属性的,这就造成了原型上属性的冗余。
那么如何规避这种冗余呢?
function SuperType(name){
this.name = name;
this.colors=["red", "blue", "green"]
}
SuperType.prototype.sayName = function(){
console.info(this.name);
}
//借用构造函数完成属性的继承
function SubType(name, age){
SuperType.call(this, name); //添加公共属性
this.age = age; //添加自有属性
}
//借用原型链实现方法的继承
function F(){}
//这样就只继承到了父类原型上的方法。
F.prototype = SuperType.prototype;
SubType.prototype = new F();
// SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
console.info(this.age);
}
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.info(instance1.colors) //red, blue, green, black
instance1.sayName(); //"Nicholas"
instance1.sayAge() //29
var instance2 = new SubType("Greg", 27);
console.info(instance2.colors) //red, blue, green
instance2.sayName() //"Greg"
instance2.sayAge() //27
这是javascript实现继承的终极方法,很多流行的前端框架内部继承的实现也是基于这一方法的。
网友评论