用比較的方式學習 ES6的class

距離上一次更新已通過去了半個多月的時間,因爲最近的需求比較多,致使總結時間變得少了不少,因此文檔更新速度明顯變慢了許多。其實這些都是理由,時間擠擠總仍是有的!在忙不也沒耽誤打上幾局遊戲不是。好了,閒話少聊,抓緊進入正題,今天主要想跟你們分享的東西,,也就是class。javascript

class的由來

在之前的文章中,曾經分享過,關於JavaScript中的對象的知識,其中提到了,構造函數建立的方式以及各個函數的優缺點,其實今天要講的class,能夠看做是構造函數的語法糖,由於它的絕大部分的功能,在ES5中都可以實現。都能實現,爲何js研究人員還要費勁巴力的去研究class呢?由於新定義的class寫法更加清晰,更像面向對象變成。說白了,能讓開發人員更好的理解構造函數。今天咱們經過對比的方式,具體的瞭解一下,ES6的class。java

對比開始

傳統的構造函數函數

function Point(x,y){
    this.x = x;
    this.y = y;
}
Point.prototype.toString = function(){
    return this.x + this.y
}
var p = new Poin(1,2);
複製代碼

ES6的class改寫post

class Point{
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
    toString(){
        return this.x + ',' + this.y
    }
}
let p = new Point(1,2);
複製代碼

經過比較,咱們能明顯的感知到,class定義的構造函數,在代碼邏輯上,明顯要好過於傳統方法。光說沒用,具體來解釋一下,ES6定義的知識點。ui

相同點:

  1. class是傳統構造函數的語法糖,Point至關於傳統函數的函數名,固然它如今也是class類的名。
  2. 使用時,能夠經過new命令,與傳統函數相同。
  3. 實例屬性,除非定義在其自己,不然都定義在原型上。
  4. 共享一個原型對象。

咱們一個個來解釋:
第一點:class是傳統函數的語法糖。this

class Point{
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
    toString(){
        return this.x + ',' + this.y
    }
}
console.log(typeof Point) //"function"
Point === Point.prototype.constructor // true
複製代碼
  1. Point 是個函數
  2. 類自己指向構造函數。由於構造函數的原型的構造屬性也指向函數自己。

第二點:經過new命令去調用。
class類,若是須要建立實例對象,必須經過new命令,不然報錯。spa

class Point{
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
    toString(){
        return this.x + ',' + this.y
    }
}
var p = Point(1,2) // Class constructor Point cannot be invoked without 'new'
複製代碼

第三點:實例屬性,除非定義在其自己,不然都定義在原型上。prototype

class Point {

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }

}

var point = new Point(2, 3);

point.toString() // (2, 3)

point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
複製代碼

在對比前,先來了解一下上面的類。能夠看到裏面有一個constructor方法,這就是構造方法,而this關鍵字則表明實例對象。也就是說,ES5 的構造函數Point,對應 ES6 的Point類的構造方法。
Point類除了構造方法,還定義了toString方法,ES6規定,類中全部的方法,都定義在原型上。
至關於code

Point.prototype = {
    constructor(){},
    toString(){}
}
複製代碼

這樣,就能理解,爲何說實例屬性,除非定義在其自己,不然都定義在原型上。若是你還不能理解,那你就去認真看一下,關於建立對象的東西,關於JavaScript中的對象
第四點:共享一個原型對象。對象

class Point {

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }

}
var p1 = new Point(2,3);
var p2 = new Point(3,2);

p1.__proto__ === p2.__proto__  //true
複製代碼

這一點,無需多言了吧。

關於定義:constructor

constructor方法是類的默認方法,經過new命令生成對象實例時,自動調用該方法。一個類必須有constructor方法,若是沒有顯式定義,一個空的constructor方法會被默認添加。

class Point {
}

// 等同於
class Point {
  constructor() {}
}
複製代碼

劃重點,constructor中的this,指向實例對象!

不一樣點

  1. class中的方法不可枚舉。
  2. 調用class必需要用new。
  3. 默認嚴格模式。
  4. 不存在變量提高。
    老規矩,一個個解釋: 第一點:class中的方法不可枚舉
class Point {
  constructor(x, y) {
    // ...
  }

  toString() {
    // ...
  }
}

Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
--------------------------------------------------------------


ar Point = function (x, y) {
  // ...
};

Point.prototype.toString = function() {
  // ...
};

Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
複製代碼

第二點:調用class必需要用new命令,這個在前面已經提到過,無需多言。

第三點:默認嚴格模式。
類和模塊的內部,默認就是嚴格模式,因此不須要使用use strict指定運行模式。只要你的代碼寫在類或模塊之中,就只有嚴格模式可用。考慮到將來全部的代碼,其實都是運行在模塊之中,因此 ES6 實際上把整個語言升級到了嚴格模式。

第四點:不存在提高。
都用了ES6了,就不要想着變量提高這個歷史遺留問題了,儘快的摒棄這些很差的方式方法,對咱們之後的開發也是十分有好處的,很少說。

靜態方法

說了這麼對跟ES5對比的東西,咱們接下倆介紹一些class類獨有的方式方法。 類至關於實例的原型,全部在類中定義的方法,都會被實例繼承。若是在一個方法前,加上static關鍵字,就表示該方法不會被實例繼承,而是直接經過類來調用,這就稱爲「靜態方法」。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
複製代碼

看見了static,就知道,它是個靜態方法。就能夠直接在Foo上調用,而不是在實例上調用。
!注意,若是靜態方法包含this關鍵字,這個this指的是類,而不是實例。

class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

Foo.bar() // hello
複製代碼

關於繼承

上個分享,我寫了關於原型繼承,沒有提到ES6的方式,只提到了傳統的函數的的繼承,感興趣的小夥伴能夠去了解一下,完全搞懂JavaScript中的繼承

爲何class也要搞繼承呢?有家產嗎?固然不是,只要是提升原型繼承的清晰度和方便性!

class Point {
}

class ColorPoint extends Point {
}
複製代碼

上面代碼定義了一個ColorPoint類,該類經過extends關鍵字,繼承了Point類的全部屬性和方法。可是因爲沒有部署任何代碼,因此這兩個類徹底同樣,等於複製了一個Point類。下面,咱們在ColorPoint內部加上代碼。

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 調用父類的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 調用父類的toString()
  }
}
複製代碼

上面代碼中,constructor方法和toString方法之中,都出現了super關鍵字,它在這裏表示父類的構造函數,用來新建父類的this對象。

子類必須在constructor方法中調用super方法,不然新建實例時會報錯。這是由於子類本身的this對象,必須先經過父類的構造函數完成塑造,獲得與父類一樣的實例屬性和方法,而後再對其進行加工,加上子類本身的實例屬性和方法。若是不調用super方法,子類就得不到this對象。

父類的靜態方法,也會被子類繼承。

class A {
  static hello() {
    console.log('hello world');
  }
}

class B extends A {
}

B.hello()  // hello world
複製代碼

如何判斷一個類是否繼承另外一個類?

Object.getPrototypeOf(ColorPoint) === Point
// true
複製代碼

Object.getPrototypeOf方法能夠用來從子類上獲取父類。

super關鍵字

super這個關鍵字,既能夠看成函數使用,也能夠看成對象使用。在這兩種狀況下,它的用法徹底不一樣。 第一種狀況: 做爲函數調用

class A {}

class B extends A {
  constructor() {
    super();
  }
}
複製代碼

上面代碼中,子類B的構造函數之中的super(),表明調用父類的構造函數。這是必須的,不然 JavaScript 引擎會報錯。

注意,super雖然表明了父類A的構造函數,可是返回的是子類B的實例,即super內部的this指的是B的實例,所以super()在這裏至關於A.prototype.constructor.call(this)。

class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B
複製代碼

第二種狀況:做爲對象調用
super做爲對象時,在普通方法中,指向父類的原型對象;在靜態方法中,指向父類。

class A {
  p() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}

let b = new B();
複製代碼

上面代碼中,子類B當中的super.p(),就是將super看成一個對象使用。這時,super在普通方法之中,指向A.prototype,因此super.p()就至關於A.prototype.p()。

這裏須要注意,因爲super指向父類的原型對象,因此定義在父類實例上的方法或屬性,是沒法經過super調用的。

class A {
  constructor() {
    this.p = 2;
  }
}

class B extends A {
  get m() {
    return super.p;
  }
}

let b = new B();
b.m // undefined
複製代碼

class繼承,最重要的就是要掌握好super的使用,這裏只是給你們作了簡單的介紹。


喜歡本文的小夥伴,別忘了點贊和關注哦!你的關注是我寫做最大的動力
小編會盡快的更新JavaScript方面的基礎、進階方面的知識,但願與你們一同進步!

相關文章
相關標籤/搜索