首先,类的继承分为es5方法和es6方法。
ES5(涉及到原型链)
-
ES5中的类用构造函数实现。
function Animal(name){ this.name=name } Animal.prototype.eat='胡萝卜' let rabbit = new Animal('小兔子') console.log(rabbit.hasOwnProprity(name)) //①true console.log(rabbit.hasOwnProprity(eat)) //②false console.log(rabbit.eat) //胡萝卜
其中,Animal就是构造函数,通过new构造了一个实例对象rabbit。
在构造函数中通过this定义的属性或者方法都会实例化到每一个对象中,作为每一个对象自身的属性和方法,这就是①处true的原因;在构造函数原型上定义的属性或方法是公共的,实例对象可以调用但是不是本身的这就是②处false的原因。
一个对象,可以使用自己本身的属性和方法,也可以使用其原型链上的属性和方法。
还有两个重要的关系:
animal._proto===Animal.prototype
Animal.prototype.constructor===Animal -
定义抽象类以及类的继承关系
- 构造函数方法(在子类的构造函数中实例化父类)
function Animal(){ if(new.target==Animal) throe new ERROR('这是一个抽象类,不能直接new实例化S!只能被继承!') this.type='动物' } function Rabbit(food){ //继承父类实例的属性和方法,可以传参的 Animal.call(this) //子类自己的属性 this.food=food }
缺点: 无法继承父类原型中的属性和方法
- 原型链继承
function Father(){ this.name = '张三' } // 父类的原型方法 Father.prototype.getName = function(){ console.log(this.name) }; function Son(){}; //让子类的原型对象指向父类实例 Son.prototype = new Father(); //根据原型链的规则,绑定一下constructor(否则constructor指向的是Father), 这一步不影响继承, 只是在用到constructor时会需要 //一开始Son.prototype身上是没有constructor的,需要根据原型链找到Father.prototype,constructor是Father;于是可以直接给Son.prototype添加constructor Son.prototype.constructor = Son;
缺点: 修改一个子类实例的
父类中的引用类型
,所有子类实例的父类中的引用类型
都会修改
原因: 修改子类实例中的父类基本类型
时,直接在子类实例上添加了一个同名属性,是属于该实例自己的,不影响其他实例。引用类型保存的都是指针,sub1和sub2指针不同但是指向同一地址。现在把地址里的内容改了,所以两者都有变化- 组合式继承。
function Animal(){ if(new.target==Animal) throe new ERROR('这是一个抽象类,不能直接new实例化S!只能被继承!') this.type='动物' } function Rabbit(food){ //继承父类实例的属性和方法 Animal.call(this) //子类自己的属性 this.food=food } //继承父类原型上的属性和方法 Tiger.prototype=new Animal()
- 寄生式组合继承
function Father(){ this.name = '张三' } // 父类的原型方法 Father.prototype.getName = function(){ console.log(this.name) }; function Son(){ Father.call(this) }; //Object.create(proto)这个函数的作用是创建一个对象,这个对象的__proto__是传入的参数 //即Son.prototype.__proto__=Father.prototype Son.prototype = Object.create(Father.prototype)
关于最后为什么不写
Tiger.prototype = Animal.prototype
?
如果这么写,二者的原型是同一个对象(浅拷贝),父类的方法子类确实可以得到,但是如果在子类原型上添加方法,同样会影响到父类,这是不符合继承特点的。现在这么写,对Tiger.prototype的操作并不会影响Animal.prototype,同时可以使Tiger.prototype可以循着原型链找到Animal.prototype的属性方法。
ES6
// class不存在变量提升,所以一定要先声明再实例化
class Classname{
// 每次new的时候就会触发
constructor(arg1,arg2){
this.arg1=arg1;
this.arg2=arg2;
//……
}
//方法 ,直接写,不需要function声明
show(){
}
//static关键字(静态方法)
// 静态函数的调用者是类本身,不是实例,也就是说this指向这个类
// 所以静态函数之间是可以用this互相调用的
static myselfFunction(){
// 这是一个静态函数
}
//用的时候要注意tag是实例的属性,不是函数,不要加()
set tag(newarg1){
this.arg1=newarg1
}
get tag(){
return this.arg1
}
}
class Son extends Classname{
constructor(arg1,arg2,arg3){
//如果直接写就会直接覆盖父类的constructor
//由于extends继承,this会混乱,因此一定要super后才能使用this
super(arg1,arg2)
this.arg3=arg3
}
//写独有方法或重写父类方法
}
let test = new Classname(arg1,arg2);