ES6中的class关键字

欢迎访问我的博客https://qqqww.com/,祝码农同胞们早日走上人生巅峰,迎娶白富美~~~

声明:本文参考业界大佬阮一峰老师的ES6标准入门

目录

  1. ES5 中的面向对象
  2. ES6 中的 class 关键字
  3. 类的实例
  4. 取值函数(getter)和存值函数(setter)
    类的继承
  5. 一些需要注意的点
  6. 不存在变量提升
  7. name
  8. Generator
  9. this
  10. 静态方法
  11. 静态属性
  12. 私有方法
  13. 私有属性
  14. 类的继承
    1. extends
    2. super

ES5 中的面向对象

// 创建一个构造函数
function People (name, age) {
    this.name = name
    this.age = age
}

// 向构造函数的原型对象中添加 say 方法
People.prototype.say = function () {
    return 'hello everyone'
}

// 实例化 People 构造函数,并保存在变量 a 中
var a = new People('zhangsan', 10)

ES6 中的 class 关键字

ES6 中的 class 类去改写上面的例子看看

class People {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    say() {
       return 'hello everyone' 
    }
}

两个例子可以说实际功能一模一样,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象,且方法之间不需要逗号分隔,加了会报错

ES6class 类,完全可以看作构造函数的另一种写法,证明如下:

class People {  }
typeof People // "function"
People.prototype.constructor === People // true

类的实例

同样的,也是用new 关键字

class People {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    say() {
       return 'hello everyone' 
    }
}
var people = new People('zhangsan', 18)

ES5 一样,实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)

类的所有实例共享一个原型对象

class People {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    say() {
       return 'hello everyone' 
    }
}
var people1 = new People('zhangsan', 18)
var people2 = new People('zhaosi', 20)
people1._proto_ === people2._proto // true

这就意味着可以通过实例的__proto__属性为“类”添加方法

class People {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    say() {
       return 'hello everyone' 
    }
}
var people1 = new People('zhangsan', 18)
var people2 = new People('zhaosi', 20)
people1._proto_.eat = function () {
    return 'banner'
}
people1.eat() // 'banner'
people2.eat() // 'banner'

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

案例借用阮一峰老师的《ES6标准入门》

class CustomHTMLElement {
    constructor(element) {
      this.element = element;
  }

  get html() {
      return this.element.innerHTML;
  }

  set html(value) {
      this.element.innerHTML = value;
  }
}

var descriptor = Object.getOwnPropertyDescriptor(
    CustomHTMLElement.prototype, "html"
);

"get" in descriptor  // true
"set" in descriptor  // true

存值函数和取值函数是定义在html属性的描述对象上面,这与 ES5 完全一致

一些需要注意的点

不存在变量提升

类不存在变量提升

new Foo(); // ReferenceError
class Foo {}

如上代码,定义Foo在后,使用在前,则报错

name

由于本质上,ES6 的类只是ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性

class People {}
People.name // "People"

name属性总是返回紧跟在class关键字后面的类名

Generator 方法

如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数

generator可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator就可以实现需要用面向对象才能实现的功能,详细请移步廖雪峰老师的generator

class Foo {
  constructor(...args) {
    this.args = args;
  }
  * [Symbol.iterator]() {
    for (let arg of this.args) {
      yield arg;
    }
  }
}

for (let x of new Foo('hello', 'world')) {
  console.log(x);
}
// hello
// world

上面代码中,Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个 Generator 函数。Symbol.iterator方法返回一个Foo类的默认遍历器,for...of循环会自动调用这个遍历器,不断返回多次,一次打印出一个参数,直到遍历出所有的参数

this

类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错

class People {
    getPeople(name = 'zhangsan') {
        this.print(`hello ${ name }`)
    }
    print(text) {
        console.log(text)
    }
}
const people = new People()
const { getPeople } = people
getPeople() // TypeError: Cannot read property 'print' of undefined

解决办法一:在constructor中绑定this

class People {
    constructor() {
        this.getPeople = this.getPeople.bind(this)
    }
    getPeople(name = 'zhangsan') {
        this.print(`hello ${ name }`)
    }
    print(text) {
        console.log(text)
    }
}
const people = new People()
const { getPeople } = people
getPeople() // 'hello zhangsan'

解决方法二:使用箭头函数

class People {
    constructor() {
        this.getPeople = (name = 'zhangsan') => this.print(`hello ${ name }`)
    }
    print(text) {
        console.log(text)
    }
}
const people = new People()
const { getPeople } = people
getPeople() // 'hello zhangsan'

静态方法

static关键字,带有static关键字的方法不会被继承,且实例不能调用,必须用过类直接调用

class People {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    static say() {
       console.log('say everyone')
    }
}
People.say() // 'hello everyone' 
var people = new People
people.say() // TypeError: people.say is not a function

注意,如果静态方法包含this关键字,这个this指的是类,而不是实例

class People {
    static hello() {
        this.say()
    }
    static say() {
        console.log('我是静态')
    }
    say() {
        console.log('我是非静态')
    }
}
People.hello() // 我是静态

静态属性

static关键字,加在属性前面,即定义了静态属性

class Foo {
  static prop = 1;
}

私有方法

私有方法是常见需求,但 ES6不提供,只能通过变通方法模拟实现

方法一:在命名上加以区别

class People {
    // 公有方法
    foo (name) {
        this._getName(name)  
    }
    
    // 私有方法
    _getName(name) {
        return this.peop = name
    }
}

上面代码中,getName方法前面的下划线,表示这是一个只限于内部使用的私有方法。但是,这种命名是不保险的,在类的外部,还是可以调用到这个方法

方法二:将私有方法移出模块

class People {
    // 公有方法
    foo (name) {
        getName.call(this, name)  
    }
}
// 私有方法
getName(name) {
    return this.peop = name
}

上述代码中,利用call使thisfoo调用了getName方法,这使得getName实际上成为了当前模块的私有方法

私有属性

请参照http://es6.ruanyifeng.com/#docs/class

类的继承

Class 可以通过extends关键字实现继承,先看个例子

extends

// 这是父类
class Person {
  constructor(name, age){
    this.name = name
    this.age = age
  }
}

// 这是子类 美国人  可继承父类的一些属性和方法
class American extends Person {
}
const a1 = new American('Jack', 20)
console.log(a1.name) // Jack

// 这是子类 中国人  可继承父类的一些属性和方法
class Chinese extends Person{
}

const c1 = new Chinese('张三', 22)
console.log(c1.name) // 张三

super

既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同

情况一:super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数

// 这是父类
class Person {
  constructor(name, age){
    this.name = name
    this.age = age
  }

  // 打招呼 的 实例方法
  sayHello(){
    console.log('大家好')
  }
}

// 这是子类 美国人 
class American extends Person {
  constructor(name, age){
    super(name, age)
  }
}

const a1 = new American('Jack', 20)
console.log(a1)
a1.sayHello()


// 这是子类 中国人
class Chinese extends Person{
  // name 姓名 
  // age 年龄
  // IDNumber 身份证号 【中国人独有的】,既然是独有的,就不适合 挂载到 父类上;
  constructor(name, age, IDNumber){
    super(name, age)
    this.IDNumber = IDNumber
  }
}

const c1 = new Chinese('张三', 22, '130428******')
console.log(c1)
c1.sayHello()

注意

  1. 如果需要添加自己独有的属性,则不能挂在到父类上
  2. 在子类中, this 只能放到 super 之后使用

情况二:super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类

// 这是父类
class Person {
  constructor(name, age){
    this.name = name
    this.age = age
  }

  // 打招呼 的 实例方法
  sayHello(){
    console.log('大家好')
  }
}

// 这是子类 美国人 
class American extends Person {
  constructor(name, age){
    super(name, age)
    console.log(super.sayHello())
  }
}

let amer = new American()

上面代码中,子类American当中的super.sayHello(),就是将super当作一个对象使用。这时,super在普通方法之中,指向Person.prototype,所以super.sayHello()就相当于Person.prototype.sayHello()



作者:这里王工头
链接:https://www.jianshu.com/p/a7e5fcb8a67f
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上一篇:2021-09-19 验证:实例对象的constructor属性指向其构造函数


下一篇:Javascript面向对象编程(二):构造函数的继承