js继承
1.原型链继承
将构造函数的原型设置为另一个构造函数的实例对象,这样就可以继承这个实例对象的所有属性和方法,然后就可以根据原型链继续往上寻找属性和方法。
//父类
function Father() {
this.color = ['red', 'green', 'blue']; //设置属性
}
//子类
function Son() {
}
Son.prototype = new Father(); //创建父类的实例,赋给子类的原型
var gSon1 = new Son();
gSon1.color.push('black');
console.log(gSon1.color); //red, green, blue, black
var gSon2.color = new Son();
console.log(gSon2.color); //red, green, blue, black
优点:
- 简单,易于实现
缺点:
- 引用类型的原型属性会被所有实例共享
- 创建子类时,无法向父类构造函数传参
2.构造函数继承
在子类型构造函数的内部调用超类型构造函数。(只要把Son.prototype的构造函数换成其他的,而不是默认的Object(),那Son便成为了那个类的子类;这就实现了自己定义的超类和子类关系。 当然Son还是Object的子类,但那是因为那个类最终也是Object的子类。 )
function Father(name){
this.name = name;
this.color = ['red', 'green', 'blue'];
}
function Son() {
Father.call(this, 'Tom');
this.size = 'big';
}
var instance1 = new Son();
instance1.color.push('black');
console.log(instance1.color); //red, green, blue, black
var instance2 = new Son();
console.log(instance2.color); //red, green, blue
console.log(instance1.name);//Tom
console.log(instance2.size);//big
优点:
- 可以向父类传递参数
- 解决了子类实例共享引用属性的问题
缺点:
- 无法实现函数复用。方法都是在父类的原型中定义方法,对于子类而言是不可见的
3.组合式继承(最常用)
将原型链和构造函数的技术组和到一块。即通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。
function Father(name){
this.name = name;
this.color = ['red', 'green', 'blue'];
}
Father.prototype.sayName = function() {
alert(this.name);
}
function Son(name){
Father.call(this, name);//继承属性
}
Son.prototype = new Father(); //继承方法
var gSon1 = new Son('Tom');
gSon1.color.push('black');
console.log(gSon1.color);//red, green, blue, black
gSon1.sayName();//Tom
var gSon2 = new Son('Jerry');
console.log(gSon2.color);//red, green, blue
gSon2.sayName();//Jerry
优点:
- 不存在引用属性共享问题
- 可以传参
- 函数可复用
缺点:
- 父类构造函数被调用了两次
4.原型式继承
借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
function object(o) {
function F(){};
F.prototype = o;
return new F();
}
//在object函数内部,先创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。
//本质上来说,object对传入其中的对象执行了一次浅复制。
function Person() {
this.name = 'Tom';
this.color= ['red', 'green', 'blue'];
}
var instance = new Person();
var anotherPerson = object(instance);
anotherPerson.age = 18;
anotherPerson.color.push('black');
console.log(anotherPerson.name);//Tom
console.log(anotherPerson.color);//red, green, blue, black
console.log(anotherPerson.age);//18
优点:
- 从已有对象衍生新对象,不需要创建自定义类型
缺点:
- 原型引用属性会被所有实例共享,因为是用整个对象来充当子类原型对象
- 无法实现代码复用
5.寄生式继承
寄生式继承是创建一个仅用于封装继承过程的函数。
function createAnother(original) {
var clone = object(original); //通过调用函数创建一个新对象
//object原型式继承种创建的函数
clone.sayHi = function() {//添加方法
alert('Hi');
};
return clone; //返回这个对象
}
var person = {
name: 'Venciki',
color: ['yellow', 'black', 'white']
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //Hi
优点:
- 跟原型式继承一样,简单易于实现,不需要创建自定义类型
缺点:
- 无法实现函数复用
6.寄生组合式继承(最佳方式)
//寄生组合式继承的基本模式
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype); //创建对象(object为原型式继承里面的方法)
//object原型式继承种创建的函数
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
在此函数内部,第一步式创建父类型原型的一个副本;第二步是为创建的副本添加constructor属性,从而弥补因重写原型而失去默认的constructo属性;最后一步,将创建的对象(即副本)赋值给子类型的原型。这样我们就可以用调用inheritPrototype()函数的语句,去替换前面例子中为子类原型赋值的语句。
function SuperType(name) {
this.name = name;
this.color = ['red', 'green', 'blue'];
}
SuperType.prototype.sayName = function() {
alert(this.name);
}
function SubType() {
SuperType.call(this, 'Tom');
}
inheritPrototype(SubType, SuperType);
var instance = new SubType();
instance.color.push('black');
console.log(instance.name);//Tom
console.log(instance.color);//red, green, blue, black
instance.sayName();//Tom