欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ES6的类与继承

程序员文章站 2022-03-08 22:20:10
...

声明类

Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。

这句话摘自阮一峰老师的博客,这句话放在ES5中可以这么说,但是放到ES6中就不那么合适,因为ES6用class关键字规范了类的声明。

先来看看在ES6之前,是怎么声明类的:

let Animal = function(type) {
    this.type = type
    this.walk = function() {
        console.log( `I am walking` )
    }
}
let monkey = new Animal('monkey')

当然也可以写成这样(建议写成以下的形式,原因上一篇博客有说 https://blog.csdn.net/juliaandjulia/article/details/108030957.)

let Animal = function(type) {
    this.type = type
}

Animal.prototype.walk = function() {
    console.log( `I am walking` )
}
let monkey = new Animal('monkey')

这里顺便打印出Animal的原型以便后续与ES6作比较

console.log(Animal.prototype)

结果如下:
Animal的原型上有一个自定义的方法walk(),还有一个构造方法constructorES6的类与继承
在 ES6 中把类的声明专业化了,不在用 function 的方式了,而是用关键字class来声明方法,用constructor声明属性。

class Animal {
    constructor(type) {
        this.type = type
    }
    walk() { //ES6对象里面函数的简洁写法 相当于ES5的 walk = function(){……}
        console.log( `I am walking` )
    }
}
let dog = new Animal('dog')
let monkey = new Animal('monkey')

很明显,从定义上就很专业了,有构造函数、方法,但是 ES6 增加了新的数据类型 class 吗?

console.log(typeof Animal) //function

可以看出,并不是,class的类型也是function,那么ES6中声明在类中的方法和ES5中定义在原型上的方法有什么不一样呢,对于这个问题,我们知道,只要是函数,就一定会有prototype对象,既然class类型也是function,那就来看看它的原型:

console.log(Animal.prototype)

结果如下:
ES6的类与继承

对比ES5可以看出,Animal.prototype上也有两个方法,也是一个是构造函数constructor,一个是自定义的方法walk,看来方法上和ES5本质上是一样的,那再来看看属性,在 ES5 中有个 API 用来判断对象的自有属性(hasOwnProperty)。

console.log(dog.hasOwnProperty('type')) //true

可以看出,这个表现也是和ES5是一样的,可以用ES5的hasOwnProperty判断属性是否存在,所以得出一个结论:class 的方式是 function 方式的语法糖,即它们本质上是一样的!

setters&&getters

对于类中的属性,可以直接在 constructor 中通过 this 直接定义,还可以直接在类的顶层来定义:

class Animal {
    constructor(type, age) {
        this.type = type
        this._age = age
    }
    get age() {
        return this._age
    }
    set age(val) {
        this._age = val
    }
}

这个代码演示了,通过 get/set 来给类定一个属性,不过貌似没有说服力。因为 age 和 _age 都是类的属性,而且是相同的含义这样做感觉没有实际用途。下面来看看当获取类的属性涉及到业务逻辑操作的时候的应用场景:

class People{
    constructor(name,age){
        this.name=name
        this.age=age
        this._sex=-1
    }
    //涉及到业务逻辑操作的时候用到
    //1:male 0:female
    get sex(){//属性
        if(this._sex===1){
            return 'male'
        }else if(this._sex===0){
            return 'female'
        }else{
            return 'error'
        }
    }
    set sex(val){
        if(val===0||val===1){
            this._sex=val
        }
    }
    showName(){
        console.log(this.name)
    }
}
let p1=new People('www',21,1)
console.log(p1)//People {name: "www", age: 21, _sex: -1}
console.log(p1.sex)//error
p1.sex=1
console.log(p1.sex)//male

此时,get/set方法就对业务逻辑进行了封装。封装了属性_sex的获取,也限制了对属性_sex的修改。

当想设置一个属性为只读时,也可以用get方法办到:

class Animal {
    constructor(type) {
        this.type = type
    }
    get addr() {
        return '北京动物园'
    }
}

有时候,我们需要设置一个私有属性(闭包),然后通过一定的规则来限制对它的修改,利用set/get就可以轻松实现。

let #age = 1
class Animal {
    constructor(type) {
        this.type = type
    }
    get age() {
        return #age
    }
    set age(val) {
        if (val > 0 && val < 10) {
            #age = val
        }
    }
}

静态方法

静态方法是面向对象最常用的功能,在ES5中直接在类上定义静态方法:

let Animal = function(type) {
    this.type = type
    this.walk = function() {
        console.log( `I am walking` )
    }
}

Animal.eat = function(food) {
    console.log( `I am eating` )
}

在ES6中,用到static关键字,一切都变得简单了许多。

class Animal {
    constructor(type) {
        this.type = type
    }
    walk() {
        console.log( `I am walking` )
    }
    static eat() {
        console.log( `I am eating` )
    }
}

继承

ES5继承中,构造方法只能继承父类内部属性/方法,不能继承原型上的属性/方法,而原型链继承只能继承原型上的属性/方法,不能继承父类内部属性/方法,所以在ES5中实现继承需要用组合式继承(构造+原型):

//父类
function Animal(name){
    this.name=name
    this.age=function(){
    	console.log(18)
    }
}
Animal.prototype.showName=function(){
    console.log('名字是'+this.name)
}
//子类
function Dog(name,color){
    Animal.call(this,name)//构造函数继承 只能继承属性 不能继承方法
    this.color=color
}
Dog.prototype=new Animal()//原型继承 只能继承方法

在ES6中,用关键字extends实现继承就可以优雅很多:

class Animal {
    constructor(type) {
        this.type = type
    }
    walk() {
        console.log( `I am walking` )
    }
    static eat() {
        console.log( `I am eating` )
    }
}

class Dog extends Animal {
  constructor () {
    super('dog')
  }
  run () {
    console.log('I can run')
  }
}
相关标签: ES es6