ES5的继承方式主要是组合继承,即构造函数继承(apply、call改变this的指向,立即执行获得父类的方法和属性)+原型链继承(父类的实例构成子类的原型对象)。
function Parent(name) {
this.name = name;
this.say = function () {
console.log('父的say');
}
}
Parent.prototype.age = 8;
Parent.prototype.sleep = function () {
console.log('父的sleep');
}
function Chlid(name) {
this.li = '子的li'
Parent.call(this, name);
//构造函数继承,先创建子类实例对象this,在将父类的方法和属性添加到this上面。
}
//原型链继承,总的说就是子类的原型对象指向父类的原型对象(内存地址相同,要想不同就深克隆再赋值)
function fn() {}
fn.prototype = Parent.prototype;
Chlid.prototype = new fn();
//对象增强,修复缺失的constructor。
Chlid.prototype.constructor = Chlid;
var p = new Chlid('小明');
console.log(p);
//Chlid { li: '子的li', name: '小明', say: [Function] }
console.log(p.age);
//8
p.sleep()
//父的sleep
ES6的继承则用语法糖class和extends。构造函数放到constructor的函数中,其余的方法放在constructor函数外,这些函数相当于Chlid.prototype中的设置。
class Parent {
//这里就是prototype的constructor(相当于)
constructor(name) {
this.name = name;
this.say = function () {
console.log('父的say');
}
}
age = 8;
//相当于Parent.prototype.sleep()
sleep() {
console.log('父的sleep');
}
}
class Chlid extends Parent {
constructor(name) {
super(name);
this.li = '子的li'
}
}
var p = new Chlid('小明');
console.log(p);
//Chlid { age: 8, name: '小明', say: [Function], li: '子的li' }
Chlid.prototype.sleep()
//父的sleep
console.log(Chlid.prototype.age);
//undefined,比较奇怪,不是在原型对象里面,也不是类的静态数据,只要实例能够访问到。
console.log(Chlid.age);
//undefined
console.log(p.age);
//8
p.sleep()
//父的sleep
//Chlid.sleep()则是错误的,不存在。
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。
ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。
ES6类继承的本质:
class A extends B{}
A.__proto__ === B;
//继承父类静态属性或方法。A.prototype.__proto__ == B.prototype;
//实例继承链。- 子类的构造函数中必定调用父类的构造函数。
- super作为对象时,在普通方法中,指向父类的原型对象( 即 B.prototype );在静态方法中,指向父类( 即 B )。