ES5及ES6中继承的写法

392 阅读4分钟

ES5中继承的写法

  • 构造函数继承可以继承属性
  • 原型继承可以继承方法

组合起来就可以既可以继承属性又可以继承方法

通过构造函数模拟继承

// 父类
// 属性定义在构造函数中
function Animal(name){
    // 实例属性(同实例化对象是有关系的)是定义在构造函数中的
    this.name = name 
}
// 方法定义在原型上
// 不管是静态属性/方法都是直接定义在类/构造函数上的
// 静态属性:如:Animal.count = 2
// 静态方法: 如:Animal.getCount = function(){}
// showName为实例方法,因为它的实例化对象可以获取到这个方法
Animal.prototype.showName = function(){
    console.log('动物的名字是:' + this.name);
}

// 子类

function Dog(name,color){
    // 实例属性是定义在类上的(同实例化对象是有关系的)
    this.color = color
    // 继承父类属性
    Animal.call(this,name)
}

Dog.prototype = new Animal()
Dog.prototype.constructor = Dog

let d1 = new Dog('wangcai','black')
console.log(d1);           // {color: 'black', name: 'wangcai'} 此处的name是继承自Animal的name
d1.showName()              // 动物的名字是:wangcai

image.png

ES6中继承

ES6中继承的写法

ES6中的继承只是ES5中继承的『语法糖』,包括关键字:class、extends、constructor、static、super、get / set

// 父类
class People{
    // 属性定义在constructor中
    constructor(name,age){
    
        this.name = name
        this.age = age
    }
    // 方法直接定义在类的顶级作用域中
    showName(){
        console.log(this.name);
    }
}

// 子类
class Coder extends People{
    // name,age为父类中的属性,company为子类的属性
    constructor(name,age,company){
    // 通过super关键字继承父类的属性
        super(name,age)    
        this.company = company
    }
    showCompany(){
        console.log(this.company);
    }
}

let c1 = new Coder('zhangsan',18,'Alibaba')
console.log(c1);
c1.showName()
c1.showCompany()

image.png

使用get和set关键字对设置属性和获取属性进行拦截

通过get和set关键字,把属性定义在类的最顶层

之前的实例实行都定义在constructor构造函数中

class People{
    // 属性定义在constructor中
    constructor(name,age){
        this.name = name
        this.age = age
    }
    get gender(){
        return 'male'
    }
    set gender(val){
        this.gender = val  //报错: mximum call stack size exceeded
    }
    // 方法直接定义在类的顶级作用域中
    showName(){
        console.log(this.name);
    }
}

// 子类
class Coder extends People{
    // name,age为父类中的属性,company为子类的属性
    constructor(name,age,company){
    // 通过super关键字继承父类的属性
        super(name,age)    
        this.company = company
    }
    showCompany(){
        console.log(this.company);
    }
}

let p1 = new People()
p1.gender = 'femail'        // cannot set property gender of #<People> which has only a getter
console.log(p1.gender);     //male

通过get关键字声明的属性表示这个属性是只读的,如果设置值会报错

如果想要赋值就要设置一个set方法.下面这种写法会及进入“死循环”

set gender(val){
    this.gender = val  //报错: mximum call stack size exceeded
}

因为只要设置值就会触发set方法,触发set方法就又会设置值,这样就进入死循环了

在set的时候我们不能直接给属性设置值,而是给一个"中间属性"设置值(_gender)

class People{
    // 属性定义在constructor中
    constructor(name,age){
        this.name = name
        this.age = age
        this._gender = -1
    }
    get gender(){  // 属性
        return this._gender
    }
    set gender(val){  // 属性
        this._gender = val
    }
    // 方法直接定义在类的顶级作用域中
    showName(){
        console.log(this.name);
    }
}

// 子类
class Coder extends People{
    // name,age为父类中的属性,company为子类的属性
    constructor(name,age,company){
    // 通过super关键字继承父类的属性
        super(name,age)    
        this.company = company
    }
    showCompany(){
        console.log(this.company);
    }
}

let p1 = new People()
p1.gender = 'femail'        
console.log(p1.gender);     //female

什么情况下使用get和set关键字?

那么使用关键字get 在类的最顶层声明的属性和constructor中声明的属性有什么区别吗?

放在constructor中的属性,当我们在取值和设置值得时候,要么是直接取,要么是直接设置,如果在取值和设置值的时候还有些业务逻辑的操作呢?在constructor中定义属性,就满足不了需求,而是应该通过get/set方法定义在类的顶层属性上,因为这样可以对属性的读写进行拦截的操作

比如: 在给gender设置值的时候,如果设置1就返回mail,如果设置1就返回femail

class People{
    // 属性定义在constructor中
    constructor(name,age){
        this.name = name
        this.age = age
        this._gender = -1
    }
    get gender(){  // 属性
        if(this._gender === 1){
            return 'male'
        }else if(this._gender === 0){
            return 'female'
        }else{
            return 'Error: "传入非法的gender属性值"'
        }
        
    }
    set gender(val){  // 属性
        if(val === 0 || val === 1){
            this._gender = val
        }
    }
    // 方法直接定义在类的顶级作用域中
    showName(){
        console.log(this.name);
    }
}

// 子类
class Coder extends People{
    // name,age为父类中的属性,company为子类的属性
    constructor(name,age,company){
    // 通过super关键字继承父类的属性
        super(name,age)    
        this.company = company
    }
    showCompany(){
        console.log(this.company);
    }
}

let p1 = new People()
// 设置属性值触发set
p1.gender = 1    
// 获取属性触发get    
console.log(p1.gender);     // male

let c1 = new Coder('zhangsan',18,'Alibaba')
c1.gender = 0              // female
console.log(c1.gender);

static关键字定义静态方法

静态属性/方法: 直接定义在类上,不能被示例调用

ES6规定:class内部只有静态的方法,不支持静态属性,如果想添加就类名.属性 = 'xxx'

class People{
    // 属性定义在constructor中
    constructor(name,age){
        this.name = name
        this.age = age
        this._gender = -1
    }
    get gender(){  // 属性
        if(this._gender === 1){
            return 'male'
        }else if(this._gender === 0){
            return 'female'
        }else{
            return 'Error: "传入非法的gender属性值"'
        }
        
    }
    set gender(val){  // 属性
        if(val === 0 || val === 1){
            this._gender = val
        }
    }
    // 方法直接定义在类的顶级作用域中
    showName(){
        console.log(this.name);
    }
    // 定义静态方法(不能被示例调用)
    static getount(){
        return 5
    }
}

// 子类
class Coder extends People{
    // name,age为父类中的属性,company为子类的属性
    constructor(name,age,company){
    // 通过super关键字继承父类的属性
        super(name,age)    
        this.company = company
    }
    showCompany(){
        console.log(this.company);
    }
}

let p1 = new People()
// 设置属性值触发set
p1.gender = 1    
// 获取属性触发get    
console.log(p1.gender);     // male
console.log(People.getount());  // 5

let c1 = new Coder('zhangsan',18,'Alibaba')
c1.gender = 0              // female
console.log(c1.gender);
console.log(Coder.getount());  // 5

注意:

class只是基于原型继承的语法糖,底层还是和ES5一样的:

console.log(typeof People); // function