距離上一次更新已通過去了半個多月的時間,因爲最近的需求比較多,致使總結時間變得少了不少,因此文檔更新速度明顯變慢了許多。其實這些都是理由,時間擠擠總仍是有的!在忙不也沒耽誤打上幾局遊戲不是。好了,閒話少聊,抓緊進入正題,今天主要想跟你們分享的東西,類,也就是class。javascript
我
在之前的文章中,曾經分享過,關於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
咱們一個個來解釋:
第一點: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
複製代碼
第二點:經過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方法是類的默認方法,經過new命令生成對象實例時,自動調用該方法。一個類必須有constructor方法,若是沒有顯式定義,一個空的constructor方法會被默認添加。
class Point {
}
// 等同於
class Point {
constructor() {}
}
複製代碼
劃重點,constructor中的this,指向實例對象!
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這個關鍵字,既能夠看成函數使用,也能夠看成對象使用。在這兩種狀況下,它的用法徹底不一樣。 第一種狀況: 做爲函數調用
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方面的基礎、進階方面的知識,但願與你們一同進步!