【ES6】浅析类与继承

145 阅读3分钟

类(class)

在ES6出现前,生成实例对象的传统方法是通过构造函数。
eg:

function Person(name,age){
    this.name = name
    this.age = age
}

如果想要挂载函数,则需要使用prototype
eg:

function Person(name,age){
    this.name = name
    this.age = age
}
Person.prototype.showName = function(){
    return `名字为:${this.name}`
}
Person.prototype.showAge = function(){
    return `年龄为:${this.age}`
}
 let p1 = new Person('Fang',20)
 console.log(p1.showAge())//年龄为:20

而ES6提供了更接近传统语言的写法,引入了Class这个概念,作为对象的模板。通过class关键字,可以定义类。
eg:

class Person{
  constructor(name,age){
    this.name = name
    this.age = age
  }
}

可以看到class里面有一个constructor方法,这就是构造方法,this指向实例对象

使用方法:
和ES5一样,直接对类使用new命令

class Person{
    constructor(name,age){
        this.name = name
        this.age = age
    }
}
new Person('Fang',20)
//Person {name: "Fang", age: 20}

如果想要挂载函数的话,直接将函数写在类中。

class Person{
    constructor(name,age){
        this.name = name
        this.age = age
    }
    showName(){
        return `名字为:${this.name}`
    }
    showAge(){
    return `年龄为:${this.age}`
    }
}
 let p1 = new Person('Fang',20)
console.log(p1.showName())//名字为:Fang

可以说,ES6的class就是一个语法糖,他的绝大部分功能和ES5一样。

ES6的类和构造函数的类型都是函数。

class Person{}
typeof Person//"function"

注意:
es6的class没有提升功能,在es5中,用函数模拟可以,因为默认函数提升。

new Person()
class Person{}
//Uncaught SyntaxError: Unexpected end of input

name属性

class Person{}
Person.name//Person

属性表达式

类中的属性名也可以使用表达式
eg:

let firstMethod = newName
class Person{
    constructor(){
        
    }
    [firstMethod](){
        
    }
}

取值函数(getter)和存值函数(setter)

class Person{
  constructor(){
  }
  get aaa(){
    return 'aaa的属性'
  }
  set aaa(value){
    console.log(`设置aaa属性,值为:${value}`)
  }
}
let p1 = new Person()
p1.aaa = '123'//设置aaa属性,值为:123
console.log(p1.aaa)//aaa的属性

静态方法

类相当于实例的原型,在类中定义的方法都会被实例继承。如果你想用类去调用类自身的方法,就必须使用静态方法,也就是在类里面要调用的方法前面加一个static。
报错情况

class Person{
  constructor(){
  }
  showName(){
    return '这是showName方法' 
  }
  aaa(){
    return '不使用静态方法'
  }
}
let p1 = new Person()
console.log(Person.aaa())//Uncaught TypeError: Person.aaa is not a function

使用静态方法

class Person{
  constructor(){
  }
  showName(){
    return '这是showName方法' 
  }
  static aaa(){
    return '使用静态方法'
  }
}
let p1 = new Person()
console.log(Person.aaa())//使用静态方法

接下来我们探讨一下static中的this指向。
js在es6中虽然定义了class,但class的本质还是function。class中static声明的变量或者函数,是属于function的属性。
回到原来的问题,static的this指向哪里?

class Person{
    static showName(){
        console.log(this)//this指向哪里
    }
}
等价于
function Person(){
    
}
Person.fun = function(){
    console.log(this)
}

从上面的代码我们可以看出,this指向Person,也就是类本身。

类的继承

es5的继承方式比较复杂:

function Person(){
  this.name = name
}
Person.prototype.showName = function(){
  return'名字是:${this.name}'
}
//子类
function Student(name,skill){
  Person.call(this,name)//继承属性
  this.skill = skill
}
Student.prototype = new Person()//继承方法
let stu1 = new Student('小明','逃学')
console.log(stu1.showName)

现在es6提供了继承的关键字:extends
这样相比es5就变得方便多了

class Person{}
class Stduent extends Person{}

es6中规定,子类的构造函数中必须执行一个super函数,也可以理解为父级的功能函数

class Person{
    constructor(){
        this.name = name
    }
}
class Student extends Person{
    constructor(){
        super(name)
        this.skill = skill
    }
    showSkill(){
        return `爷的技能为:${this.skill}`
    }
}

如果出现子类的函数名与父类函数名相同的情况下,又想一起执行,可在子类的同名函数里使用super关键字来调用父级函数执行

class Person{
    constructor(name){
        this.name = name
    }
    showName(){
        console.log('父类showName')
        return `父类showName`
    }
}
class Student extends Person{
    constructor(name,skill){
        super(name)
        this.skill = skill
    }
    showName(){
        super.showName()
        console.log('子类showName')
    }
    showSkill(){
        return `爷的技能为:${this.skill}`
    }
}
let p1 = new Student('fang','run')
p1.showName()
//父类showName
//子类showName

es5的继承,原理是先创建子类的实例对象this,然后再将父类的添加到子类的this上面(parent.apply(this))。而es6的继承则是先将父类的属性和实例方法添加到this上面,也就是要先调用super方法的原因,再用子类的constructor来增加this的属性和方法。

父类的静态方法,也会被子类继承
eg:

class Person{
    static hello(){
        console.log('hello')
    }
}
class Student extends Person{}
Student.hello()//hello

Object.getPrototypeOf()

Object.getPrototypeOf()可以用来从子类获取父类

Object.getPrototypeOf(Student) === Person//true

类的prototype和__proto__

Class同时具有prototype和__proto__。
1.子类的__proto__属性表示构造函数的继承,指向父类
2.子类的prototype的__proto__属性表示方法的继承,指向父类的prototype属性
eg:

class Person{}
class Student extends Person{}
Student.__proto__ = Person//true
Student.prototype.__proto__ = Person.prototype//true

这样是因为类的继承时按照以下方式实现的:

class Person {
}

class Student {
}

// Person 的实例继承 Student 的实例
Object.setPrototypeOf(Student.prototype, Person.prototype);
//相当于
Student.prototype.__proto__ = Person.prototype

// Student 继承 Person 的静态属性
Object.setPrototypeOf(Student, Person);
//相当于
Student.__proto__ = Person

如果您觉得我的文章有用,欢迎点赞和关注,也欢迎光临我的个人博客github.com/BokFang