美文网首页
ES6中的class关键字

ES6中的class关键字

作者: 这里王工头 | 来源:发表于2019-01-30 02:21 被阅读0次

    欢迎访问我的博客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()

    相关文章

      网友评论

          本文标题:ES6中的class关键字

          本文链接:https://www.haomeiwen.com/subject/ydqqsqtx.html