原型链继承
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log('parent name:', this.name);
}
function Child(name) {
this.name = name;
}
Child.prototype = new Parent('father');
Child.prototype.constructor = Child;
Child.prototype.sayName = function() {
console.log('child name:', this.name);
}
var child = new Child('son');
child.sayName(); // child name: son
Child.prototype = new Parent('father');这样Child.prototype == {name: "father", sayName: ƒ},同时childe.__proto__ == {name: "father", sayName: ƒ}- 问题1:
child.constructor == Parent(name) {this.name = name;}是为什么
先看看
parent.constructor == Parent(name) {this.name = name;}是怎么来的。首先它找自己的constructor方法,自己没有就找parent.__proto__发现上面有,就这个属性指向的就是Parent(name) {this.name = name;}。child自己没有,child.__proto__ = {name: "father", sayName: ƒ}也没有,就继续沿着__proto__找,最终找到发现指向Parent(name) {this.name = name;}对象,解决。
- 问题2: 为什么不直接Child.prototype = Parent.prototype
很明显这样的话,Child.prototype 和 Parent.prototype就是指向的同一个对象,他们就变成了同一个类,共享方法了,比如上诉代码
Child.prototype.sayName = function(){console.log('child name:', this.name);}会直接把Parent.sayName()给修改掉
- 子类对象找方法是沿着原型链也就是__proto__,先找自己的,再找父类的
- 缺点一: Child.prototype.sayName方法必须写在
Child.prototype = new Parent('father');之后,不然就会被覆盖掉。 - 缺点二:无法继承属性
组合式继承
function Parent(age,height) {
this.age = age;
}
Parent.prototype.sayName = function() {
console.log('parent name:', this.name);
}
Parent.prototype.doSomething = function() {
console.log('parent do something!');
}
function Child(name, parentName) {
Parent.call(this, parentName); // 第二次调用
this.name = name;
}
Child.prototype = new Parent(); // 第一次调用
Child.prototype.constructor = Child;
Child.prototype.sayName = function() {
console.log('child name:', this.name);
}
var child = new Child('son');
child.sayName(); // child name: son
child.doSomething();
- 相比上面增加了
Parent.call(this, parentName);意图很明显,在Child这个this上绑上Parent的属性,为后面的属性继承做铺垫 - 还增加了
Child.prototype.constructor = Child;让它有自己的constructor,child.__proto__指向的就是Child,这样来实现对属性的继承 - 问题1:组合继承使用过程中父类会被调用两次:一次是创建子类型的时候,另一次是在子类型构造函数的内部。不觉得很过分嘛?
第一次调用的意义就是获得Parent上的方法,本来直接
Child.prototype = Parent.prototype就可以,但是上面说过这样的问题。正版解决方法:使用过渡的中间类,让这个中间类的prototype指向Parent.prototype,这时需要new这个过度类,看似意义不打,但是如果Parent是一个很大的对象,这就有用了。代码看第三种继承方式
寄生组合式继承
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log('parent name:', this.name);
}
function Child(name, parentName) {
Parent.call(this, parentName);
this.name = name;
}
// ****** 修改部分
function create(proto) {
function F(){}
F.prototype = proto;
return new F();
}
Child.prototype = create(Parent.prototype);
// ******
Child.prototype.sayName = function() {
console.log('child name:', this.name);
}
Child.prototype.constructor = Child;
var parent = new Parent('father');
parent.sayName(); // parent name: father
var child = new Child('son', 'father');
child.sayName(); // child name: son
- 思路就是上面的:用一个 F 空的构造函数去取代执行了 Parent 这个构造函数。中心思想:只需要父类原型的上的方法,不需要构造它的属性
ES6 继承
class Parent {
constructor(name) {
this.name = name;
}
doSomething() {
console.log('parent do something!');
}
sayName() {
console.log('parent name:', this.name);
}
}
class Child extends Parent {
constructor(name, parentName) {
super(parentName);
this.name = name;
}
sayName() {
console.log('child name:', this.name);
}
}
const child = new Child('son', 'father');
child.sayName(); // child name: son
child.doSomething(); // parent do something!
const parent = new Parent('father');
parent.sayName(); // parent name: father
- ES6的继承就和java的继承一样了,使用extends关键字