偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

JavaScript中的四種枚舉方式

開(kāi)發(fā) 前端
當(dāng)一個(gè)變量有一個(gè)來(lái)自有限的預(yù)定義常量的值時(shí),使用枚舉是很方便的。枚舉使你不必使用魔法數(shù)字和字符串(這被認(rèn)為是一種反模式)。

字符串和數(shù)字具有無(wú)數(shù)個(gè)值,而其他類(lèi)型如布爾值則是有限的集合。

一周的日子(星期一,星期二,...,星期日),一年的季節(jié)(冬季,春季,夏季,秋季)和基本方向(北,東,南,西)都是具有有限值集合的例子。

當(dāng)一個(gè)變量有一個(gè)來(lái)自有限的預(yù)定義常量的值時(shí),使用枚舉是很方便的。枚舉使你不必使用魔法數(shù)字和字符串(這被認(rèn)為是一種反模式)。

讓我們看看在JavaScript中創(chuàng)建枚舉的四種好方法(及其優(yōu)缺點(diǎn))。

基于對(duì)象的枚舉

枚舉是一種數(shù)據(jù)結(jié)構(gòu),它定義了一個(gè)有限的具名常量集。每個(gè)常量都可以通過(guò)其名稱(chēng)來(lái)訪問(wèn)。

讓我們來(lái)考慮一件T恤衫的尺寸:Small,Medium,和Large。

在JavaScript中創(chuàng)建枚舉的一個(gè)簡(jiǎn)單方法(雖然不是最理想的)是使用一個(gè)普通的JavaScript對(duì)象。

const Sizes = {
  Small: 'small',
  Medium: 'medium',
  Large: 'large',
}

const mySize = Sizes.Medium

console.log(mySize === Sizes.Medium) // logs true

Sizes是一個(gè)基于JavaScript對(duì)象的枚舉,它有三個(gè)具名常量:Sizes.Small、Sizes.Medium以及Sizes.Large。

Sizes也是一個(gè)字符串枚舉,因?yàn)榫呙A康闹凳亲址?small' ,'medium',以及 'large'。

要訪問(wèn)具名常量值,請(qǐng)使用屬性訪問(wèn)器。例如,Sizes.Medium的值是'medium'。

枚舉的可讀性更強(qiáng),更明確,并消除了對(duì)魔法字符串或數(shù)字的使用。

優(yōu)缺點(diǎn)

普通的對(duì)象枚舉之所以吸引人,是因?yàn)樗芎?jiǎn)單:只要定義一個(gè)帶有鍵和值的對(duì)象,枚舉就可以了。

但是在一個(gè)大的代碼庫(kù)中,有人可能會(huì)意外地修改枚舉對(duì)象,這將影響應(yīng)用程序的運(yùn)行。

const Sizes = {
  Small: 'small',
  Medium: 'medium',
  Large: 'large',
}

const size1 = Sizes.Medium
const size2 = Sizes.Medium = 'foo' // Changed!

console.log(size1 === Sizes.Medium) // logs false

Sizes.Medium 枚舉值被意外地改變。

size1,雖然被初始化為Sizes.Medium,但不再等同于Sizes.Medium!

普通對(duì)象的實(shí)現(xiàn)沒(méi)有受到保護(hù),因此無(wú)法避免這種意外的改變。

讓我們仔細(xì)看看字符串和symbol枚舉。以及如何凍結(jié)枚舉對(duì)象以避免意外改變的問(wèn)題。

枚舉值類(lèi)型

除了字符串類(lèi)型,枚舉值可以是一個(gè)數(shù)字:

const Sizes = {
  Small: 0,
  Medium: 1,
  Large: 2
}

const mySize = Sizes.Medium

console.log(mySize === Sizes.Medium) // logs true

上述例子中,Sizes枚舉是數(shù)值枚舉,因?yàn)橹刀际菙?shù)字:0,1,2。

你也可以創(chuàng)建symbol枚舉:

const Sizes = {
  Small: Symbol('small'),
  Medium: Symbol('medium'),
  Large: Symbol('large')
}

const mySize = Sizes.Medium

console.log(mySize === Sizes.Medium) // logs true

使用symbol的好處是,每個(gè)symbol都是唯一的。這意味著,你總是要通過(guò)使用枚舉本身來(lái)比較枚舉:

const Sizes = {
  Small: Symbol('small'),
  Medium: Symbol('medium'),
  Large: Symbol('large')
}

const mySize = Sizes.Medium

console.log(mySize === Sizes.Medium)     // logs true
console.log(mySize === Symbol('medium')) // logs false

使用symbol枚舉的缺點(diǎn)是JSON.stringify()將symbol字符串化為null、undefined,或者跳過(guò)有symbol作為值的屬性:

const Sizes = {
  Small: Symbol('small'),
  Medium: Symbol('medium'),
  Large: Symbol('large')
}

const str1 = JSON.stringify(Sizes.Small)
console.log(str1) // logs undefined

const str2 = JSON.stringify([Sizes.Small])
console.log(str2) // logs '[null]'

const str3 = JSON.stringify({ size: Sizes.Small })
console.log(str3) // logs '{}'

在下面的例子中,我將使用字符串枚舉。但是你可以自由地使用你需要的任何值類(lèi)型。

如果你可以自由選擇枚舉值類(lèi)型,就用字符串吧。字符串比數(shù)字和symbol更容易進(jìn)行調(diào)試。

基于Object.freeze()枚舉

保護(hù)枚舉對(duì)象不被修改的一個(gè)好方法是凍結(jié)它。當(dāng)一個(gè)對(duì)象被凍結(jié)時(shí),你不能修改或向該對(duì)象添加新的屬性。換句話說(shuō),這個(gè)對(duì)象變成了只讀。

在JavaScript中,Object.freeze()工具函數(shù)可以凍結(jié)一個(gè)對(duì)象。讓我們來(lái)凍結(jié)Sizes枚舉:

const Sizes = Object.freeze({
  Small: 'small',
  Medium: 'medium',
  Large: 'large',
})

const mySize = Sizes.Medium

console.log(mySize === Sizes.Medium) // logs true

const Sizes = Object.freeze({ ... }) 創(chuàng)建一個(gè)凍結(jié)的對(duì)象。即使被凍結(jié),你也可以自由地訪問(wèn)枚舉值: const mySize = Sizes.Medium。

優(yōu)缺點(diǎn)

如果一個(gè)枚舉屬性被意外地改變了,JavaScript會(huì)拋出一個(gè)錯(cuò)誤(在嚴(yán)格模式下):

const Sizes = Object.freeze({
  Small: 'Small',
  Medium: 'Medium',
  Large: 'Large',
})

const size1 = Sizes.Medium
const size2 = Sizes.Medium = 'foo' // throws TypeError

語(yǔ)句const size2 = Sizes.Medium = 'foo' 對(duì) Sizes.Medium 屬性進(jìn)行了意外的賦值。

因?yàn)镾izes是一個(gè)凍結(jié)的對(duì)象,JavaScript(在嚴(yán)格模式下)會(huì)拋出錯(cuò)誤:

TypeError: Cannot assign to read only property 'Medium' of object <Object>

凍結(jié)的對(duì)象枚舉被保護(hù)起來(lái),不會(huì)被意外地改變。

不過(guò),還有一個(gè)問(wèn)題。如果你不小心把枚舉常量拼錯(cuò)了,那么結(jié)果將是未undefined:

const Sizes = Object.freeze({
  Small: 'small',
  Medium: 'medium',
  Large: 'large',
})

console.log(Sizes.Med1um) // logs undefined

Sizes.Med1um表達(dá)式(Med1um是Medium的錯(cuò)誤拼寫(xiě)版本)結(jié)果為未定義,而不是拋出一個(gè)關(guān)于不存在的枚舉常量的錯(cuò)誤。

讓我們看看基于代理的枚舉如何解決這個(gè)問(wèn)題。

基于proxy枚舉

一個(gè)有趣的,也是我最喜歡的實(shí)現(xiàn),是基于代理的枚舉。

代理是一個(gè)特殊的對(duì)象,它包裹著一個(gè)對(duì)象,以修改對(duì)原始對(duì)象的操作行為。代理并不改變?cè)紝?duì)象的結(jié)構(gòu)。

枚舉代理攔截對(duì)枚舉對(duì)象的讀和寫(xiě)操作,并且:

  • 當(dāng)訪問(wèn)一個(gè)不存在的枚舉值時(shí),會(huì)拋出一個(gè)錯(cuò)誤。
  • 當(dāng)一個(gè)枚舉對(duì)象的屬性被改變時(shí)拋出一個(gè)錯(cuò)誤

下面是一個(gè)工廠函數(shù)的實(shí)現(xiàn),它接受一個(gè)普通枚舉對(duì)象,并返回一個(gè)代理對(duì)象:

// enum.js
export function Enum(baseEnum) {  
  return new Proxy(baseEnum, {
    get(target, name) {
      if (!baseEnum.hasOwnProperty(name)) {
        throw new Error(`"${name}" value does not exist in the enum`)
      }
      return baseEnum[name]
    },
    set(target, name, value) {
      throw new Error('Cannot add a new value to the enum')
    }
  })
}

代理的get()方法攔截讀取操作,如果屬性名稱(chēng)不存在,則拋出一個(gè)錯(cuò)誤。

set()方法攔截寫(xiě)操作,但只是拋出一個(gè)錯(cuò)誤。這是為保護(hù)枚舉對(duì)象不被寫(xiě)入操作而設(shè)計(jì)的。

讓我們把sizes對(duì)象枚舉包裝成一個(gè)代理:

import { Enum } from './enum'

const Sizes = Enum({
  Small: 'small',
  Medium: 'medium',
  Large: 'large',
})

const mySize = Sizes.Medium

console.log(mySize === Sizes.Medium) // logs true

代理枚舉的工作方式與普通對(duì)象枚舉完全一樣。

優(yōu)缺點(diǎn)

然而,代理枚舉受到保護(hù),以防止意外覆蓋或訪問(wèn)不存在的枚舉常量:

import { Enum } from './enum'

const Sizes = Enum({
  Small: 'small',
  Medium: 'medium',
  Large: 'large',
})

const size1 = Sizes.Med1um         // throws Error: non-existing constant
const size2 = Sizes.Medium = 'foo' // throws Error: changing the enum

Sizes.Med1um拋出一個(gè)錯(cuò)誤,因?yàn)镸ed1um常量名稱(chēng)在枚舉中不存在。

Sizes.Medium = 'foo' 拋出一個(gè)錯(cuò)誤,因?yàn)槊杜e屬性已被改變。

代理枚舉的缺點(diǎn)是,你總是要導(dǎo)入枚舉工廠函數(shù),并將你的枚舉對(duì)象包裹在其中。

基于類(lèi)的枚舉

另一種有趣的創(chuàng)建枚舉的方法是使用一個(gè)JavaScript類(lèi)。

一個(gè)基于類(lèi)的枚舉包含一組靜態(tài)字段,其中每個(gè)靜態(tài)字段代表一個(gè)枚舉的常量。每個(gè)枚舉常量的值本身就是該類(lèi)的一個(gè)實(shí)例。

讓我們用一個(gè)Sizes類(lèi)來(lái)實(shí)現(xiàn)sizes枚舉:

class Sizes {
  static Small = new Sizes('small')
  static Medium = new Sizes('medium')
  static Large = new Sizes('large')
  #value

  constructor(value) {
    this.#value = value
  }

  toString() {
    return this.#value
  }
}

const mySize = Sizes.Small

console.log(mySize === Sizes.Small)  // logs true
console.log(mySize instanceof Sizes) // logs true

Sizes是一個(gè)代表枚舉的類(lèi)。枚舉常量是該類(lèi)的靜態(tài)字段,例如,static Small = new Sizes('small')。

Sizes類(lèi)的每個(gè)實(shí)例也有一個(gè)私有字段#value,它代表枚舉的原始值。

基于類(lèi)的枚舉的一個(gè)很好的優(yōu)點(diǎn)是能夠在運(yùn)行時(shí)使用instanceof操作來(lái)確定值是否是枚舉。例如,mySize instanceof Sizes結(jié)果為真,因?yàn)閙ySize是一個(gè)枚舉值。

基于類(lèi)的枚舉比較是基于實(shí)例的(而不是在普通、凍結(jié)或代理枚舉的情況下的原始比較):

class Sizes {
  static Small = new Sizes('small')
  static Medium = new Sizes('medium')
  static Large = new Sizes('large')
  #value

  constructor(value) {
    this.#value = value
  }

  toString() {
    return this.#value
  }
}

const mySize = Sizes.Small

console.log(mySize === new Sizes('small')) // logs false

mySize(即Sizes.Small)不等于new Sizes('small')。

Sizes.Small和new Sizes('small'),即使具有相同的#value,也是不同的對(duì)象實(shí)例。

優(yōu)缺點(diǎn)

基于類(lèi)的枚舉不能受到保護(hù),以防止覆蓋或訪問(wèn)不存在的枚舉具名常量。

class Sizes {
  static Small = new Sizes('small')
  static Medium = new Sizes('medium')
  static Large = new Sizes('large')
  #value

  constructor(value) {
    this.#value = value
  }

  toString() {
    return this.#value
  }
}

const size1 = Sizes.medium         // a non-existing enum value can be accessed
const size2 = Sizes.Medium = 'foo' // enum value can be overwritten accidentally

但你可以控制新實(shí)例的創(chuàng)建,例如,通過(guò)計(jì)算在構(gòu)造函數(shù)內(nèi)創(chuàng)建了多少個(gè)實(shí)例。然后在創(chuàng)建超過(guò)3個(gè)實(shí)例時(shí)拋出一個(gè)錯(cuò)誤。

當(dāng)然,最好讓你的枚舉實(shí)現(xiàn)盡可能的簡(jiǎn)單。枚舉的目的是為了成為普通的數(shù)據(jù)結(jié)構(gòu)。

總結(jié)

在JavaScript中,有4種創(chuàng)建枚舉的好方法。

最簡(jiǎn)單的方法是使用一個(gè)普通的JavaScript對(duì)象:

const MyEnum = {
  Option1: 'option1',
  Option2: 'option2',
  Option3: 'option3'
}

普通的對(duì)象枚舉適合小型項(xiàng)目或快速演示。

第二種選擇,如果你想保護(hù)枚舉對(duì)象不被意外覆蓋,則可以使用凍結(jié)的對(duì)象:

const MyEnum = Object.freeze({
  Option1: 'option1',
  Option2: 'option2',
  Option3: 'option3'
})

凍結(jié)的對(duì)象枚舉適合于中型或大型項(xiàng)目,你要確保枚舉不會(huì)被意外地改變。

第三種選擇是代理方法:

export function Enum(baseEnum) {  
  return new Proxy(baseEnum, {
    get(target, name) {
      if (!baseEnum.hasOwnProperty(name)) {
        throw new Error(`"${name}" value does not exist in the enum`)
      }
      return baseEnum[name]
    },
    set(target, name, value) {
      throw new Error('Cannot add a new value to the enum')
    }
  })
}
import { Enum } from './enum'

const MyEnum = Enum({
  Option1: 'option1',
  Option2: 'option2',
  Option3: 'option3'
})

代理枚舉適用于中型或大型項(xiàng)目,以更好地保護(hù)你的枚舉不被覆蓋或訪問(wèn)不存在的命名常量。

代理的枚舉是我個(gè)人的偏好。

第四種選擇是使用基于類(lèi)的枚舉,其中每個(gè)命名的常量都是類(lèi)的實(shí)例,并作為類(lèi)的靜態(tài)屬性被存儲(chǔ):

class MyEnum {
  static Option1 = new MyEnum('option1')
  static Option2 = new MyEnum('option2')
  static Option3 = new MyEnum('option3')
  #value

  constructor(value) {
    this.#value = value
  }

  toString() {
    return this.#value
  }
}

如果你喜歡類(lèi)的話,基于類(lèi)的枚舉是可行的。然而,基于類(lèi)的枚舉比凍結(jié)的或代理的枚舉保護(hù)得更少。

你還知道哪些在JavaScript中創(chuàng)建枚舉的方法?

本文譯自:https://dmitripavlutin.com/javascript-enum/[1]

以上就是本文的全部?jī)?nèi)容,如果對(duì)你有所幫助,歡迎點(diǎn)贊、收藏、轉(zhuǎn)發(fā)~

參考資料

[1]

https://dmitripavlutin.com/javascript-enum/:https://dmitripavlutin.com/javascript-enum/

責(zé)任編輯:武曉燕 來(lái)源: 前端F2E
相關(guān)推薦

2022-03-25 14:47:24

Javascript數(shù)據(jù)類(lèi)型開(kāi)發(fā)

2020-06-12 08:28:29

JavaScript開(kāi)發(fā)技術(shù)

2021-07-14 10:31:15

JavaScript開(kāi)發(fā) 技巧

2021-07-27 10:49:10

SpringSecurity權(quán)限

2020-06-17 08:31:10

權(quán)限控制Spring Secu

2010-07-28 13:54:42

Flex數(shù)據(jù)綁定

2017-04-17 19:31:03

Android多線程

2013-06-14 15:24:57

Android開(kāi)發(fā)移動(dòng)開(kāi)發(fā)數(shù)據(jù)存儲(chǔ)方式

2021-12-22 09:34:01

Golagn配置方式

2014-12-25 09:41:15

Android加載方式

2009-06-11 17:22:03

操作xml方式Java

2013-10-17 09:25:52

2019-10-24 07:42:28

Java引用GC

2022-03-15 11:01:39

KubernetesLinux平滑升級(jí)

2021-12-01 15:40:40

節(jié)日開(kāi)源剪貼畫(huà)

2015-04-02 16:54:52

災(zāi)難恢復(fù)VDI災(zāi)難恢復(fù)

2015-04-13 11:39:26

VDI災(zāi)難恢復(fù)

2025-05-09 09:39:45

2024-01-17 13:56:00

Redis節(jié)點(diǎn)映射關(guān)系

2022-07-04 08:29:13

electron通信
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)