ES6新特征之Class

在JS里面,它实际上是没有类的概念的,我们如果想要实现一个面向对象的一个编程,那么我们只能够借助构造函数,但是构造函数有着自身的缺点,首先第一点它的写法不清晰,我们需要继承的话就需要手动的去操作prototype;其次它面向对象的思想也不是很强烈,所以在ES6它封装了一层构造函数的语法糖,就叫Class,封装后有了一个更清晰的写法,而且更加像一个面向对象编程语言。

1:基本概念

构造函数语法糖

  • 更加清晰的写法
  • 面向对象编程
    在ES5中:
      // ES5 的构造函数
      function Position(x, y) {
        this.x = x;
        this.y = y;
      }
      
      const position = new Position(1, 1);
    
    在ES6中:
    在ES6中,首先有一个class修饰符,class将告诉编辑器声明了一个名叫Position的class。
    class是构造函数的语法糖,我们怎么来看呢?我们可以看一下这个构造函数它的prototype的constructor是否是本身,如果是本身那就说明class的行为跟构造函数是保持一致的。
        class Position {
           constructor(x, y) {
             this.x = x;
             this.y = y;
           }
         }
         Position === Position.prototype.constructor;
         /* true */
         const position = new Position(1, 1);
    

类的所有方法定义在类的prototype属性

在ES5里面,比如说我们在构造函数里面定义了一些方法,那么我们在实例里面是有这一个方法的,但是ES6不一样。在Class里面,所有的方法全部都定义到了prototype属性上面,也就是除了this.之外的其他都定义在了prototype属性上面去。

    class Position {
       constructor(x, y) {
         this.x = x;
         this.y = y;
       }
    
       scan() {
         console.log(this.x, this.y);
       }
    }
    Position.prototype;
      
    /*
    {
      constructor: class Position
      scan: ƒ scan()
      __proto__: Object
    }
    */

内部方法不可枚举

内部的方法都到prototype上面去,但是这个prototype的方法却是不可枚举的,也就是说它的枚举属性是false。
如果这个时候我们想要得到这个枚举,想要知道它有哪些方法的话,我们就需要使用到getOwnPropertyNames。

   class Position {
     constructor(x, y) {
       this.x = x;
       this.y = y;
     }
     scan() {
       console.log(this.x, this.y);
     }
   }
   
   Object.keys(Position.prototype);
   /* [] */
   
   Object.getOwnPropertyNames(Position.prototype);
   /* ["constructor", "scan"] */

this指向 - 默认指向实例本身

    class Position {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
      scan() {
        console.log(this.x, this.y);
      }
    }
    
    const position = new Position(1, 1);
    
    // 调用自身扫描
    position.scan();
    // 1 1

解构出来再去调用this指向就不一致了,会出问题报错:

      const { scan } = position;
      scan();
      /* Cannot read property 'x' of undefined */

对于上面的this在解构后调用出现的指向问题出错我们可以有两种解决办法:
第一种方法,借助于箭头函数:
箭头函数在定义时this就已经指定了,也就是它什么定义的和在哪里定义的,它的this就是什么。

      class Position {
         constructor(x, y) {
           this.x = x;
           this.y = y;
         }
         // 将scan()函数改为一个箭头函数,this永远指向定义时的位置
         scan = () => {
           console.log(this.x, this.y);
         }
      }
      
      const position = new Position(1, 1);
      const { scan } = position;
      scan();
      /* 1 1 */

第二种方法,手动的bind:

      class Position {
         constructor(x, y) {
           this.x = x;
           this.y = y;
           this.scan = this.scan.bind(this);
         }
         scan() {
           console.log(this.x, this.y);
         }
      }
      const position = new Position(1, 1);
      const { scan } = position;
      scan();
      /* 1 1 */

2:方法属性

constructor 构造器,首先它是一个required,也就是它是一个必须的,如果我们没有传入它,那么就会默认生成一个空的一个constructor。

constructor - required - 默认为空函数

  • new命令使用时调用一次constructor
        class Position {
           constructor(x, y) {
              this.x = x;
              this.y = y;
              console.log('constructor');
           }
        }
        
        const position = new Position(1, 1);
        /* constructor */
  • 默认返回实例对象
         class Position {
            constructor(x, y) {
              return Object.create({ name: 'Eric' });
            }
          }
        
          const position = new Position(1, 1);
          控制台输入:position
          // 得到一个空对象:{}
          
          position instanceof Position;
          /* false */

类的实例

  • new命令创建
  • 实例的属性除this之外全部定义在原型对象上
      hasOwnProperty();

getter与setter - 拦截器,拦截某个属性属性的存取行为

get是拦截它的取,set是拦截它的存,getter和setter必须是成对出现的,而且我们设置时需要设置getter和setter应该是哪一个,应该是当前的对象不存在的,否则就会报一个堆栈溢出,因为它会有一个循环调用的问题。

      class Position {
        constructor(x, y) {
          this.x = x;
          this.y = y;
        }
      
        get z() {
          return this.x;
        }
      
        set z(value) {
          this.x = 2;
        }
      }
      
      const position = new Position(1, 1);

static - 静态方法 - 不能被实例继承 - this指向类

static字面意思理解就是静态,它不能被实例继承,也就是说在实例里面是不能调用静态方法的,它只能通过类本身来调用,这时这个this指向的是当前class本身。

      class Position {
        constructor(x, y) {
          this.x = x;
          this.y = y;
          this.z = 100;
        }
      
        static scan() {
          console.log(this);
        }
      }
      
      const position = new Position(1, 1);
      
      position.scan();
      /* position.scan is not a function */
      
      
      Position.scan();
      /*
      class Position {
        constructor(x, y) {
          this.x = x;
          this.y = y;
          this.z = 100;
        }
      
        static scan() {
          console.log(this);
        }
      }
      */

属性新写法

在之前的代码里面属性 x 和 y 都是定义在constructor内部的,这样实际上是比较清晰的但是有一个提案:把属性全部放在构造函数的最顶层,我们可以直接在构造函数的内部来定义一些变量,当然这个变量是不需要加修饰符的,这时候就默认加到了this的对象上面去。

      class Count {
        x = 0;
      
        reduce() {
          this.x = this.x - 1;
          console.log(this.x);
        }
      
        increment() {
          this.x = this.x + 1;
          console.log(this.x);
        }
      }
      
      const count = new Count();
      
      count.increment();
      count.increment();
      count.increment();
      count.increment();
      
      // 1
         2
         3
         4 

3:类的继承

    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
    
      scan = () => {
        console.log(this.x, this.y);
      }
    }
    // SubPoint 继承了 Point
    class SubPoint extends Point {
    
    }
    const subPoint = new SubPoint(1, 1);
    
    subPoint;
    /*
    {
      scan: () => { console.log(this.x, this.y); }
      x: 1
      y: 1
      __proto__: Point
    }
    */

子类必须调用super方法

      class Point {
        constructor(x, y) {
          this.x = x;
          this.y = y;
        }
      
        scan = () => {
          console.log(this.x, this.y);
        }
      }
      
      class SubPoint extends Point {
        constructor(...rest) {
          // super(...rest)
        }
      }
      const subPoint = new SubPoint(1, 1);
      
      /* Must call super constructor in derived class before accessing 'this' or returning from derived constructor */

Object.getPrototypeOf - 可以在类中获取父类实例

      class Point {
        constructor(x, y) {
          this.x = x;
          this.y = y;
        }
      
        scan = () => {
          console.log(this.x, this.y);
        }
      }
      
      class SubPoint extends Point {
        constructor(...rest) {
          super(...rest)
        }
      }
      const subPoint = new SubPoint(1, 1);
      
      Object.getPrototypeOf(subPoint);
      /*
      Point {
        constructor: class SubPoint
        __proto__: Object
      }
      */

你可能感兴趣的:(ES6新特征之Class)