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屬性上面去。ui

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。this

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指向就不一致了,會出問題報錯:prototype

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

對於上面的this在解構後調用出現的指向問題出錯咱們能夠有兩種解決辦法: 第一種方法,藉助於箭頭函數: 箭頭函數在定義時this就已經指定了,也就是它什麼定義的和在哪裏定義的,它的this就是什麼。rest

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:code

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
      }
      */
相關文章
相關標籤/搜索