1、原型链继承
子类的原型指向父类的实例,这就是原型链的指向方式。
function Parent () {
this.name = 'haha';
}
Parent.prototype.getName = function () {
console.log(this.name);
}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
console.log(child1.getName()) // haha
2、借用构造函数(经典继承)
function Parent() {
this.x = 100;
this.y = 199;
}
Parent.prototype.fn = function() {}
function Child() {
this.d = 100;
Parent.call(this); //构造函数中的this就是当前实例
}
var p = new Parent();
var c = new Child();
console.log(p) //Parent {x: 100, y: 199}
console.log(c) //Child {d: 100, x: 100, y: 199}
优点:通过Funtion的call方法改变this指向,子类继承了父类的私有属性和方法,
避免了引用类型的属性被所有实例共享。还可以在 Child 中向 Parent 传参,覆盖父类的私有属性,如下:
function Parent(z) {
this.x = 100;
this.y = 199;
this.z=z;
}
Parent.prototype.fn = function() {}
function Child(z) {
Parent.call(this,z); //构造函数中的this就是当前实例
}
var p = new Parent(666);
var c = new Child(777);
console.log(p) //Parent {x: 100, y: 199, z: 666}
console.log(c) //Child {x: 100, y: 199, z: 777}
3、组合继承
结合了原型链继承和构造函数经典继承,拥有公有和私有的方法属性。
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name);//new Child()的时候,调用一次构造函数
this.age = age;
}
//子类的原型指向父类的实例对象
Child.prototype = new Parent(); //这里也调用一次构造函数
//改变子类的原型的constructor属性为Child,而不是Parent
Child.prototype.constructor = Child;
var child1 = new Child('Mary', '18');
child1.colors.push('black');
console.log(child1.name); // Mary
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
优点:融合原型链继承和构造函数的优点,是 JavaScript 中常用的继承模式。
缺点:每次都会调用两次构造函数:一次是在创建子类的原型的时候,另一次是在子类型构造函数的内部。子类最终会包含父类型对象的全部实例属性,但我们不得不在调用子类构造函数时重写这些属性。