继承
继承:子类继承父类中的属性和方法「目的是让子类的实例能够调取父类中的属性和方法」
1.原型继承
让父类中的属性和方法在子类实例的原型链上;
Child.prototype=new Parent()
;Child.prototype.constructor=Child
;
function Parent(){
this.names=['Jack','Tom'];
}
Parent.prototype.getName=function(index){
return this.names[index];
}
function Child(){}
//原型链继承
Child.prototype=new Parent();
Child.prototype.constructor=Child;
const child1=new Child();
child1.names.push('Lucy');
console.log(child1.getName(0));//Jack
console.log(child1.names);//[ 'Jack', 'Tom', 'Lucy' ]
const child2=new Child();
console.log(child2.getName(0));//Jack
console.log(child2.names);//[ 'Jack', 'Tom', 'Lucy' ]
-
特点:
- 1.不像其他语言中的继承一样「其他语言的继承一般是拷贝继承」,它是把父类的原型放到子类实例的原型链上,实例想调取这个方法,是基于
__proto__
原型链查找机制完成的; - 2.子类可以重写父类上的方法「这样会导致父类其他的实例也受到影响」
- 3.父类中私有或者公有属性方法,最后都会变为子类中公有的属性和方法;
- 1.不像其他语言中的继承一样「其他语言的继承一般是拷贝继承」,它是把父类的原型放到子类实例的原型链上,实例想调取这个方法,是基于
-
存在的问题:
- 1.引用类型的属性被所有实例共享;
- 2.创建实例时,不能给父类传值;
2.构造函数继承「经典继承」
Child方法中把Parent当做普通函数执行,让Parent中的this指向Child的实例,相当于给Child的实例设置了很多私有属性和方法
特点:
- 1.只能继承父类私有的属性和方法「因为是把Parent当作普通函数执行,和其他原型上的属性和方法没有关系」
- 2.父类私有的变为子类私有的
function Parent(){
this.names=['Jack','Tom'];
}
Parent.prototype.getNames=function(){
return this.names;
}
function Child(){
//把Parent当普通函数执行,this是Child的实例
Parent.call(this);
}
const c1=new Child();
c1.names.push('Lucy');
console.log(c1.names);//[ 'Jack', 'Tom', 'Lucy' ]
const c2=new Child();
console.log(c2.names);//[ 'Jack', 'Tom' ]
c1.getNames();//会报错
function Animal(name){
this.name=name;
}
function Dog(name){
//把Animal当普通函数执行,this是Dog的实例 「相当于给子类添加了name私有属性」
Animal.call(this,name);// dog.name=name
}
const d=new Dog('dog');
console.log(d.name);//dog
-
优点:
- 1.避免了引用类型属性被所用实例共享问题
- 2.可以在子类中向父类传值
-
缺点:
- 方法都在构造函数中定义,每次创建实例都会创建一遍方法
3.组合继承
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
return this.name;
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
//这个不要忘记
Child.prototype.constructor = Child;
const c1 = new Child('Jack', 18);
c1.colors.push('black');
console.log(c1.name);//Jack
console.log(c1.age);//18
console.log(c1.colors);//[ 'red', 'blue', 'green', 'black' ]
const c2 = new Child('Tom', 20);
console.log(c2.name);//Tom
console.log(c2.age);//20
console.log(c2.colors);//[ 'red', 'blue', 'green' ]
- 优点:
- 融合原型链继承和构造函数的优点,JS中常用的继承模式
3.寄生组合式继承
寄生组合式继承:call继承+类似于原型继承
特点:
父类私有,公有的属性和方法分别是子类实例的私有和公有属性和方法「推荐使用」
function Parent(name){
this.name=name;
}
Parent.prototype.getName=function(){
console.log(this.name);
}
function Child(name,age){
//Parent执行,继承私有属性
Parent.call(this,name);
this.age=age;
}
// Object.create(Obj):创建一个空对象,让空对象的__proto__指向Obj
Child.prototype=Object.create(Parent.prototype);
Child.prototype.constructor=Child;
const c=new Child('Jack',18);
console.log(c.name);//Jack
console.log(c.age);//18
c.getName();//正常打印
实现Object.create(obj)方法:Object.create方法会创建一个空对象,空对象的
__proto__
指向obj
Object.create({name:'Jack'});
{
__proto__:{
name:'Jack'
}
}
Object.create=function(obj){
function Fn(){}
Fn.prototype=obj;
return new Fn();
}
4.ES6继承
- 如果子类有
constructor
需要调用super()
,相当于执行了A.call(this)
; - 如果没有
constructor
,默认底层已经调用了super()
class A {
constructor(name){
this.name=name;
}
getName(){
return this.name
}
static sex='男';
}
class B extends A{
constructor(name,age){
super(name);
this.age=age;
}
getAge(){
return this.age;
}
}
const b=new B('Jack',18);
console.log(b);//{ name: 'Jack', age: 18 }