讀Javascript最佳實(shí)踐(8)-ES6類及類屬性

翻譯自《javascript-best-practice》

ES6為JavaScript引入了類,但它們對(duì)于復(fù)雜的應(yīng)用程序來(lái)說(shuō)太簡(jiǎn)單了。Class Fields(也稱為類屬性)旨在提供具有私有和靜態(tài)成員的更簡(jiǎn)單的構(gòu)造函數(shù)。該提案目前處于TC39第3階段:候選人,可能出現(xiàn)在ES2019(ES10)中。

在我們更詳細(xì)地檢查類字段之前,快速回顧一下ES6類是很有用的。

ES6類基礎(chǔ)知識(shí)

JavaScript的原型繼承模型可能會(huì)讓開(kāi)發(fā)人員感到困惑,因?yàn)樗麄兞私釩 ++,C#,Java和PHP等語(yǔ)言中使用的經(jīng)典繼承。JavaScript類主要是語(yǔ)法糖,但它們提供了更熟悉的面向?qū)ο缶幊谈拍睢?/p>

類是一個(gè)模板,用于定義該類型的對(duì)象的行為方式。下面的Animal類定義了通用動(dòng)物(類通常用首字母大寫表示,以區(qū)別于對(duì)象和其他類型):

class Animal {

  constructor(name = 'anonymous', legs = 4, noise = 'nothing') {

    this.type = 'animal';
    this.name = name;
    this.legs = legs;
    this.noise = noise;

  }

  speak() {
    console.log(`${this.name} says "${this.noise}"`);
  }

  walk() {
    console.log(`${this.name} walks on ${this.legs} legs`);
  }

}

類聲明以嚴(yán)格模式執(zhí)行;沒(méi)有必要添加'use strict'。

創(chuàng)建此類型的對(duì)象時(shí)運(yùn)行constructor方法,并且通常在其中定義初始屬性。speak()并且walk()是添加其他功能的方法。

現(xiàn)在可以使用new關(guān)鍵字從此類創(chuàng)建對(duì)象:

const rex = new Animal('Rex', 4, 'woof');
rex.speak();          // Rex says "woof"
rex.noise = 'growl';
rex.speak();          // Rex says "growl"

Getter和Setter

Setter是用于僅定義值的特殊方法。同樣,Getters是用于僅返回值的特殊方法。例如:

class Animal {
constructor(name = 'anonymous', legs = 4, noise = 'nothing') {

    this.type = 'animal';
    this.name = name;
    this.legs = legs;
    this.noise = noise;

  }

  speak() {
    console.log(`${this.name} says "${this.noise}"`);
  }

  walk() {
    console.log(`${this.name} walks on ${this.legs} legs`);
  }

  // setter
  set eats(food) {
    this.food = food;
  }

  // getter
  get dinner() {
    return `${this.name} eats ${this.food || 'nothing'} for dinner.`;
  }
}

const rex = new Animal('Rex', 4, 'woof');
rex.eats = 'anything';
console.log( rex.dinner );  // Rex eats anything for dinner.

MDN setter The set syntax binds an object property to a function to be called when there is an attempt to set that property.
MDN getter The get syntax binds an object property to a function that will be called when that property is looked up.

子對(duì)象或子類

使用一個(gè)類作為另一個(gè)類的基礎(chǔ)通常是實(shí)用的。如果我們主要?jiǎng)?chuàng)建狗對(duì)象,那就Animal太通用了,我們必須每次都指定相同屬性值的4(legs)和“woof”(noise)。

Dog類可以使用extends從Animal類繼承所有屬性和方法??梢愿鶕?jù)需要添加或刪除特定于狗的屬性和方法:

class Dog extends Animal {

  constructor(name) {

    // call the Animal constructor
    super(name, 4, 'woof');
    this.type = 'dog';

  }

  // override Animal.speak
  speak(to) {

    super.speak();
    if (to) console.log(`to ${to}`);

  }

}

super指的是父類,通常在constructor中調(diào)用。在此示例中,Dog speak()方法會(huì)覆蓋在Animal類中定義的方法。

Dog現(xiàn)在可以創(chuàng)建對(duì)象實(shí)例:

const rex = new Dog('Rex');
rex.speak('everyone');      // Rex says "woof" to everyone

rex.eats = 'anything';
console.log( rex.dinner );  // Rex eats anything for dinner.

靜態(tài)方法和屬性

使用static關(guān)鍵字定義方法允許在類上調(diào)用它而不創(chuàng)建對(duì)象實(shí)例。JavaScript不像其他語(yǔ)言那樣支持靜態(tài)屬性,但可以向類定義添加屬性(a class本身就是一個(gè)JavaScript對(duì)象?。?。

該Dog級(jí)可適應(yīng)保留多少狗對(duì)象已經(jīng)創(chuàng)建了一個(gè)數(shù):

class Dog extends Animal {

  constructor(name) {

    // call the Animal constructor
    super(name, 4, 'woof');
    this.type = 'dog';

    // update count of Dog objects
    Dog.count++;

  }

  // override Animal.speak
  speak(to) {

    super.speak();
    if (to) console.log(`to ${to}`);

  }

  // return number of dog objects
  static get COUNT() {
    return Dog.count;
  }

}

// static property (added after class is defined)
Dog.count = 0;

類的靜態(tài)屬性的COUNT的getter返回創(chuàng)建的狗的數(shù)量:

console.log(`Dogs defined: ${Dog.COUNT}`); // Dogs defined: 0

const don = new Dog('Don');

console.log(`Dogs defined: ${Dog.COUNT}`); // Dogs defined: 1

const kim = new Dog('Kim');

console.log(`Dogs defined: ${Dog.COUNT}`); // Dogs defined: 2

有關(guān)更多信息,請(qǐng)參閱面向?qū)ο蟮腏avaScript:深入了解ES6類。

ESnext類字段

類字段(class field)提議允許在類的頂部初始化屬性:

class MyClass {

  a = 1;
  b = 2;
  c = 3;

}

這相當(dāng)于:

class MyClass {

  constructor() {
    this.a = 1;
    this.b = 2;
    this.c = 3;
  }

}

初始化程序在任何構(gòu)造函數(shù)運(yùn)行之前執(zhí)行(假設(shè)仍然需要構(gòu)造函數(shù))。

靜態(tài)類字段

類字段允許在類中聲明靜態(tài)屬性。例如:

class MyClass {

  x = 1;
  y = 2;
  static z = 3;

}

console.log( MyClass.z ); // 3

不雅的ES6等價(jià)物:

class MyClass {

  constructor() {
    this.x = 1;
    this.y = 2;
  }

}

MyClass.z = 3;

console.log( MyClass.z ); // 3

私有類字段

ES6類中的所有屬性默認(rèn)都是公共的,可以在類外檢查或修改。在Animal上面的示例中,無(wú)法阻止直接修改food,而不是通過(guò)調(diào)用eats的setter:

class Animal {
constructor(name = 'anonymous', legs = 4, noise = 'nothing') {

    this.type = 'animal';
    this.name = name;
    this.legs = legs;
    this.noise = noise;

  }

  set eats(food) {
    this.food = food;
  }

  get dinner() {
    return `${this.name} eats ${this.food || 'nothing'} for dinner.`;
  }

}

const rex = new Animal('Rex', 4, 'woof');
rex.eats = 'anything';      // standard setter
rex.food = 'tofu';          // bypass the eats setter altogether
console.log( rex.dinner );  // Rex eats tofu for dinner.

其他語(yǔ)言允許private聲明屬性。這在ES6中是不可能的,盡管開(kāi)發(fā)人員可以使用下劃線約定(_propertyName)來(lái)解決它。

在ESnext中,使用哈希#前綴定義私有類字段:

class MyClass {

  a = 1;          // .a is public
  #b = 2;         // .#b is private
  static #c = 3;  // .#c is private and static

  incB() {
    this.#b++;
  }

}

const m = new MyClass();

m.incB(); // runs OK
m.#b = 0; // error - private property cannot be modified outside class

請(qǐng)注意,雖然TC39第2階段:提案草案建議#在名稱上使用哈希前綴,但無(wú)法定義私有方法,getter和setter 。例如:

class MyClass {

  // private property
  #x = 0;

  // private method (can only be called within the class)
  #incX() {
    this.#x++;
  }

  // private setter (can only be called within the class)
  set #setX(x) {
    this.#x = x;
  }

  // private getter (can only be called within the class)
  get #getX() {
    return this.$x;
  }

}

直接受益:更清潔的反應(yīng)代碼!

React組件通常具有與DOM事件關(guān)聯(lián)的方法。為了確保this解析組件,相應(yīng)的bind每個(gè)方法都是必要的。例如:

class App extends Component {

  constructor() {

    super();

    state = { count: 0 };

    // bind all methods
    this.incCount = this.incCount.bind(this);
  }

  incCount() {
    this.setState(ps => ({ count: ps.count + 1 }));
  }

  render() {

    return (
      <div>
        <p>{ this.state.count }</p>
        <button onClick={this.incCount}>add one</button>
      </div>
    );

  }
}

如果incCount定義為類字段,則可以使用ES6 =>將其設(shè)置為函數(shù),該箭頭會(huì)自動(dòng)將其綁定到定義對(duì)象。該state還可以聲明為類字段,因此沒(méi)有構(gòu)造是必需的:

class App extends Component {

  state = { count: 0 };

  incCount = () => {
    this.setState(ps => ({ count: ps.count + 1 }));
  };

  render() {

    return (
      <div>
        <p>{ this.state.count }</p>
        <button onClick={this.incCount}>add one</button>
      </div>
    );

  }
}

今天使用類字段

瀏覽器或Node.js當(dāng)前不支持類字段。但是,可以使用Babel來(lái)轉(zhuǎn)換語(yǔ)法,Babel在使用Create React App時(shí)默認(rèn)啟用?;蛘?,可以使用以下終端命令安裝和配置Babel:

mkdir class-properties
cd class-properties
npm init -y
npm install --save-dev babel-cli babel-plugin-transform-class-properties
echo '{ "plugins": ["transform-class-properties"] }' > .babelrc

build在以下scripts部分添加命令package.json:

"scripts": {
  "build": "babel in.js -o out.js"
},

然后運(yùn)行npm run build以將ESnext文件in.js轉(zhuǎn)換為跨瀏覽器兼容的文件out.js。

已經(jīng)提出了 Babel對(duì)私有方法,getter和setter的支持。

類字段:是一種改進(jìn)?

ES6類定義過(guò)于簡(jiǎn)單化。類字段應(yīng)該有助于提高可讀性并啟用一些有趣的選項(xiàng)。我并不特別喜歡使用哈希#來(lái)表示私有成員,但是如果沒(méi)有它就會(huì)產(chǎn)生意外的行為和性能問(wèn)題(有關(guān)詳細(xì)說(shuō)明,請(qǐng)參閱JavaScript的新#private類字段)。

也許這是不言而喻的,但無(wú)論如何我都會(huì)說(shuō):本文中討論的概念可能會(huì)有所變化,可能永遠(yuǎn)不會(huì)實(shí)現(xiàn)!也就是說(shuō),JavaScript類字段具有實(shí)際的好處,并且興趣正在上升。這是一個(gè)安全的賭注。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,657評(píng)論 1 32
  • 概要 64學(xué)時(shí) 3.5學(xué)分 章節(jié)安排 電子商務(wù)網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,851評(píng)論 0 3
  • class的基本用法 概述 JavaScript語(yǔ)言的傳統(tǒng)方法是通過(guò)構(gòu)造函數(shù),定義并生成新對(duì)象。下面是一個(gè)例子: ...
    呼呼哥閱讀 4,203評(píng)論 3 11
  • 函數(shù)和對(duì)象 1、函數(shù) 1.1 函數(shù)概述 函數(shù)對(duì)于任何一門語(yǔ)言來(lái)說(shuō)都是核心的概念。通過(guò)函數(shù)可以封裝任意多條語(yǔ)句,而且...
    道無(wú)虛閱讀 4,950評(píng)論 0 5
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML標(biāo)準(zhǔn)。 注意:講述HT...
    kismetajun閱讀 28,817評(píng)論 1 45

友情鏈接更多精彩內(nèi)容