js的继承方式有很多,本文主要介绍以下几种:构造函数继承、原型链继承、组合继承、es6中的class继承这四种。
一、构造函数+call继承
构造函数一般命名时首字母大写,用来区分于普通函数,内部使用的this对象来指向即将要生成的实例对象,使用New来生成实例对象。
// 这是一个构造函数
function Animal(name) {
this.name = name;
this.showName = function() {
console.log(this.name);
};
}
function Cat(name) {
Animal.call(this, name);
//Animal.bind(this)(name)
//Animal.apply(this, [name])
}
let cat = new Cat("我是一只猫");
cat.showName();
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
补充知识:
call、apply、bind方法的作用都是改变函数的执行环境,第一个参数传入上下文执行环境(this),然后传入函数执行所需的参数。
优点:
- 可以在子类型构造函数中向Parent传递参数
- 避免了引用类型的属性被所有实例共享
缺点:
- 只能继承构造函数中的属性和方法,而无法继承原型链上的属性和方法
- 实例并不是父类的实例,只是子类的实例
二、原型链继承
利用原型链来实现继承就是让父类的一个实例作为子类的原型
// 这是一个构造函数
function Animal(name) {
this.name = name;
this.showName = function() {
console.log(this.name);
};
}
function Cat() {}
//父类的一个实例作为子类的原型
Cat.prototype = new Animal("我是一只猫");
// 注意这里new Animal()生成的父类对象并没有constructor属性,故需添加上
Cat.prototype.constructor = Cat;
let cat = new Cat();
cat.showName();
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
优点:
- 能够继承父类构造函数及其原型链上的全部属性和方法
- 实例是子类的实例,也是父类的实例
缺点:
- 在创建实例时,不能向Parent传参
三、组合继承
组合构造函数及原型链两种继承方法,从而解决他们存在的缺点,基本思路就是使用原型链继承原型上的属性和方法,通过构造函数继承实例属性,这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。
// 这是一个构造函数
function Animal(name) {
this.name = name;
this.showName = function() {
console.log(this.name);
};
}
function Cat(name) {
// 继承父类的属性
Animal.apply(this, [name]); //第二次调用
}
//父类的一个实例作为子类的原型,继承父类的方法
Cat.prototype = new Animal();
// 注意这里new Animal()生成的父类对象并没有constructor属性,故需添加上
Cat.prototype.constructor = Cat;
let cat = new Cat("我是一只猫");
cat.showName();
优点:
- 解决了原型链继承和构造函数继承存在的问题
缺点:
- 父类的构造函数被调用两次
四、class继承
利用ES6中的extends,写法更加面向对象,原理还是原型链
class Animal {
constructor(name) {
this.name = name;
}
showName() {
console.log(this.name);
}
}
class Cat extends Animal {
constructor(name) {
super(name); //不能少,而且必须写在this前边
this.type = "宠物";
}
showType() {
console.log(this.type);
}
}
let cat = new Cat("我是一只猫");
cat.showName();
cat.showType();
优点:
- 实现更简单,且不存在不能传参等问题
补充知识super :
super关键字可以当作函数
super()
使用,也可以当作对象super
使用1.当做函数使用
在constructor中,表示父类的构造函数,并且子类的构造函数必须执行一次super,即
super(name)
相当parent.prototype.constructor.call(this,name)
constructor(name) { super(name); this.type = "宠物"; }
super()也可写在子类的构造函数中,写在自定义函数中会报错。
2.当做对象使用
在静态方法中指向父类,在普通方法中指向父类的原型