• JS 类总结


    class 关键字是 ES6 新增的。类(class)是ECMAScript新的基础性语法糖,本质上还是一个函数,但实际上它使用的仍然是原型和构造函数的概念。并且类受块级作用域限制。

    class Person { }
    console.log(Person);// class Person { }
    
    • 1
    • 2

    定义类

    类定义中的代码默认都在严格模式下执行。类包含如下可选内容:

    • 构造函数方法
    • 实例方法
    • 获取函数
    • 设置函数
    • 静态类方法。

    空的类定义照样有效。

    • 类声明
      类定义没有像函数那样的声明提升。
    class Person {} 
    
    • 1
    • 类表达式
      类表达式不能在它们被求值前使用。
    const Animal = class {}; 
    
    • 1

    类的组成

    构造函数

    在使用 new 操作符创建类的新实例时,会调用 constructor 方法,主要作用是初始化数据。不是必需的,不定义构造函数相当于将构造函数定义为空函数。

    使用 new 调用类的构造函数会执行如下操作。

    1. 在内存中创建一个新对象。
    2. 这个新对象内部的 [[Prototype]] 指针被赋值为构造函数的 prototype 属性。
    3. 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)。
    4. 执行构造函数内部的代码(给新对象添加属性)。
    5. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
    class Animal { }
    class Person {
      constructor(name) {
        this.name = name;
      }
    }
    let animal = new Animal();
    let person = new Person('孤城浪人');
    console.log(animal); //Animal {}
    console.log(person); //Person {name: '孤城浪人'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    默认情况下,constructor 会在执行之后返回当前实例对象。如果 constructor 显示的 return 了一个对象,那么这个对象不会通过 instanceof 操作符检测出跟类有关联,因为这个对象的原型指针并没有被修改。

    class Person {
      constructor(name) {
        this.foo = name;
        if (name) {
          return {
            name: 'XXXXX'
          };
        }
      }
    }
    let p1 = new Person(),
      p2 = new Person('孤城浪人');
    console.log(p1); // Person {foo: undefined}
    console.log(p1 instanceof Person); // true 
    console.log(p2); // {name: 'XXXXX'}
    console.log(p2 instanceof Person); // false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    我们可以调用实例的 constructor 方法,因为他就是普通函数,只是必须使用 new 关键字调用罢了。

    class Person {
      constructor(name) {
        this.foo = name;
      }
    }
    let p1 = new Person('孤城浪人'),
      p2 = new p1.constructor('XXXXX');
    console.log(p1); // Person {foo: undefined}
    console.log(p2); // Person {name: 'XXXXX'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    类可以立即调用

    const person = new class Person {
      constructor(name) {
        this.name = name;
      }
    }('孤城浪人')
    console.log(person);// Person {name: '孤城浪人'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    成员

    每次通过 new 调用类标识符时,都会执行类构造函数,这也就意味着在 constructor 中定义的成员每个实例都是一份新的,不会共享。但是不是在 constructor 中定义的成员(不能是原始值或对象)是定义在类的原型上,所有类的实例共享。

    const person = class Person {
      constructor(name) {
        // 不共享
        this.name = name;
      }
      // 定义在类原型上共享
      sayName() {
        console.log(this.name);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    类定义也支持获取和设置访问器,方法名支持字符串、符号或计算的值。

    class Person {
      set name(newName) {
        this.name_ = newName;
      }
      get name() {
        return this.name_;
      }
      ['sa' + 'y']() {
        console.log('xxxx');
      }
    }
    new Person().say();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    静态方法

    类可以定义静态方法。它的调用不依赖于实例,静态方法的方法名不能重复

    class Person {
      static sayHellow() {
        console.log('Hellow');
      }
      static say() {
        console.log('xxxx');
      }
    }
    Person.say();//Hellow
    Person.sayHellow();//xxxx
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    迭代

    类支持在原型和类本身定义生成器方法,因此可以添加一个默认的迭代器,把类实例变成可迭代对象。

    class Person {
      constructor() {
        this.names = ['孤城浪人', 'xxxx']
      }
      // [Symbol.iterator]() {
      //   return this.names.entries();
      // }
      *[Symbol.iterator]() {
        yield* this.names.entries();
      }
    }
    const person = new Person();
    for (let item of person) {
      console.log(item);
    }
    // [0, '孤城浪人']
    // [1, 'xxxx']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    继承

    这是类最优秀的特性,原生支持继承,但是本质上还是利用了原型链。使用 extends 关键字,就可以继承任何拥有[[Construct]]和原型的对象,前面说过类就是一个语法糖,他本质上是一个函数,所以 extends 不仅可以继承一个类,也可以继承普通的构造函数。并且 this 永远指向当前实例。

    function Person() { };
    class Father extends Person {
      say() {
        console.log(this);
      }
    }
    class Son extends Father { }
    const son = new Son();
    console.log(son instanceof Father);//true
    console.log(son instanceof Person);//true
    son.say();//Son{}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这个例子可以看到 Father 继承 Person,Son 继承 Father,所以最后 Son 的实例的原型链中有 Father
    和 Person 的原对象,因此 instanceof 返回 true。

    super

    如果子类需要调用父类的构造函数 constructor 初始化数据,那么只能在子类的构造函数中使用 super 关键字,此外在实例方法和静态方法内部也可以使用 super 关键字。(ES6 给类构造函数和静态方法添加了内部特性 [[HomeObject]] 指向定义该方法的对象。 super 始终会定义为 [[HomeObject]] 的原型)

    注意:子类若定义了 constructor 则一定要调用 super,并且不要在调用 super 之前引用 this,否则会抛出 ReferenceError,

    class Father {
      constructor(name) {
        this.name = name;
      }
      say() {
        console.log(this);
      }
    }
    class Son extends Father {
      constructor() {
        super('孤城浪人');
      }
      sayName() {
        console.log('sayName')
        super.say();
      }
    }
    const son = new Son();
    son.sayName();
    // sayName
    // Son { name: '孤城浪人' }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    如果子类没有构造函数,在实例化子类时会自动调用 super(),而且会把所有传给子类的参数作为 super 的参数传入。

    class Father {
      constructor(name) {
        this.name = name;
      }
      say() {
        console.log(this);
      }
    }
    class Son extends Father {}
    const son = new Son('孤城浪人');
    son.say();
    //Son { name: '孤城浪人' }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    抽象基类

    抽象基类就是可供其他类继承,但本身不会被实例化的类。可以在构造函数中使用 new.target 属性阻止抽象基类实例化,也可以在其中强制子类要定义某个方法。

    class Father {
      constructor(name) {
        if (new.target == Father) {
          throw new Error('不能实例化');
        }
        if (!this.say) {
          throw new Error('缺少say方法');
        }
      }
    }
    class Son extends Father {
      constructor(name) {
        super();
        this.name = name;
      }
      say() {
        console.log(this.name);
      }
    }
    const son = new Son('孤城浪人');
    son.say();//孤城浪人
    new Father()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    我是孤城浪人,一名正在前端路上摸爬滚打的菜鸟,欢迎你的关注。

  • 相关阅读:
    C++建造者模式管理内存问题
    Redis-使用java代码操作Redis->java连接上redis,java操作redis的常见类型数据存储,redis中的项目应用
    联想笔记本win10无法启动vmware虚拟机
    TreeMap类的继承关系简介说明
    第六课:NIO简介
    自学Python 35 闭包:函数和利用引用环境组合而成的实体
    spring 常见面试题
    视觉SLAM十四讲(高翔版本),ch4章节部分笔记
    400G QSFP-DD SR8光模块应用场景解析
    十大排序算法详解-上篇:比较排序算法【python 动态图解】
  • 原文地址:https://blog.csdn.net/weixin_46015333/article/details/127912598