日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747


當一個變量有一個來自有限的預定義常量的值時,使用枚舉是很方便的。枚舉使你不必使用魔法數字和字符串(這被認為是一種反模式)。

字符串和數字具有無數個值,而其他類型如布爾值則是有限的集合。

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

當一個變量有一個來自有限的預定義常量的值時,使用枚舉是很方便的。枚舉使你不必使用魔法數字和字符串(這被認為是一種反模式)。

讓我們看看在JAVAScript中創建枚舉的四種好方法(及其優缺點)。

基于對象的枚舉

枚舉是一種數據結構,它定義了一個有限的具名常量集。每個常量都可以通過其名稱來訪問。

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

在JavaScript中創建枚舉的一個簡單方法(雖然不是最理想的)是使用一個普通的JavaScript對象。

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

const mySize = Sizes.Medium

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

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

Sizes也是一個字符串枚舉,因為具名常量的值是字符串:'small' ,'medium',以及 'large'。

要訪問具名常量值,請使用屬性訪問器。例如,Sizes.Medium的值是'medium'。

枚舉的可讀性更強,更明確,并消除了對魔法字符串或數字的使用。

優缺點

普通的對象枚舉之所以吸引人,是因為它很簡單:只要定義一個帶有鍵和值的對象,枚舉就可以了。

但是在一個大的代碼庫中,有人可能會意外地修改枚舉對象,這將影響應用程序的運行。

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!

普通對象的實現沒有受到保護,因此無法避免這種意外的改變。

讓我們仔細看看字符串和symbol枚舉。以及如何凍結枚舉對象以避免意外改變的問題。

枚舉值類型

除了字符串類型,枚舉值可以是一個數字:

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

const mySize = Sizes.Medium

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

上述例子中,Sizes枚舉是數值枚舉,因為值都是數字:0,1,2。

你也可以創建symbol枚舉:

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

const mySize = Sizes.Medium

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

使用symbol的好處是,每個symbol都是唯一的。這意味著,你總是要通過使用枚舉本身來比較枚舉:

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枚舉的缺點是JSON.stringify()將symbol字符串化為null、undefined,或者跳過有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 '{}'

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

如果你可以自由選擇枚舉值類型,就用字符串吧。字符串比數字和symbol更容易進行調試。

基于Object.freeze()枚舉

保護枚舉對象不被修改的一個好方法是凍結它。當一個對象被凍結時,你不能修改或向該對象添加新的屬性。換句話說,這個對象變成了只讀。

在JavaScript中,Object.freeze()工具函數可以凍結一個對象。讓我們來凍結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({ ... }) 創建一個凍結的對象。即使被凍結,你也可以自由地訪問枚舉值: const mySize = Sizes.Medium。

優缺點

如果一個枚舉屬性被意外地改變了,JavaScript會拋出一個錯誤(在嚴格模式下):

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

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

語句const size2 = Sizes.Medium = 'foo' 對 Sizes.Medium 屬性進行了意外的賦值。

因為Sizes是一個凍結的對象,JavaScript(在嚴格模式下)會拋出錯誤:

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

凍結的對象枚舉被保護起來,不會被意外地改變。

不過,還有一個問題。如果你不小心把枚舉常量拼錯了,那么結果將是未undefined:

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

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

Sizes.Med1um表達式(Med1um是Medium的錯誤拼寫版本)結果為未定義,而不是拋出一個關于不存在的枚舉常量的錯誤。

讓我們看看基于代理的枚舉如何解決這個問題。

基于proxy枚舉

一個有趣的,也是我最喜歡的實現,是基于代理的枚舉。

代理是一個特殊的對象,它包裹著一個對象,以修改對原始對象的操作行為。代理并不改變原始對象的結構。

枚舉代理攔截對枚舉對象的讀和寫操作,并且:

  • 當訪問一個不存在的枚舉值時,會拋出一個錯誤。
  • 當一個枚舉對象的屬性被改變時拋出一個錯誤

下面是一個工廠函數的實現,它接受一個普通枚舉對象,并返回一個代理對象:

// 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()方法攔截讀取操作,如果屬性名稱不存在,則拋出一個錯誤。

set()方法攔截寫操作,但只是拋出一個錯誤。這是為保護枚舉對象不被寫入操作而設計的。

讓我們把sizes對象枚舉包裝成一個代理:

import { Enum } from './enum'

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

const mySize = Sizes.Medium

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

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

優缺點

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

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拋出一個錯誤,因為Med1um常量名稱在枚舉中不存在。

Sizes.Medium = 'foo' 拋出一個錯誤,因為枚舉屬性已被改變。

代理枚舉的缺點是,你總是要導入枚舉工廠函數,并將你的枚舉對象包裹在其中。

基于類的枚舉

另一種有趣的創建枚舉的方法是使用一個JavaScript類。

一個基于類的枚舉包含一組靜態字段,其中每個靜態字段代表一個枚舉的常量。每個枚舉常量的值本身就是該類的一個實例。

讓我們用一個Sizes類來實現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是一個代表枚舉的類。枚舉常量是該類的靜態字段,例如,static Small = new Sizes('small')。

Sizes類的每個實例也有一個私有字段#value,它代表枚舉的原始值。

基于類的枚舉的一個很好的優點是能夠在運行時使用instanceof操作來確定值是否是枚舉。例如,mySize instanceof Sizes結果為真,因為mySize是一個枚舉值。

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

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,也是不同的對象實例。

優缺點

基于類的枚舉不能受到保護,以防止覆蓋或訪問不存在的枚舉具名常量。

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

但你可以控制新實例的創建,例如,通過計算在構造函數內創建了多少個實例。然后在創建超過3個實例時拋出一個錯誤。

當然,最好讓你的枚舉實現盡可能的簡單。枚舉的目的是為了成為普通的數據結構。

總結

在JavaScript中,有4種創建枚舉的好方法。

最簡單的方法是使用一個普通的JavaScript對象:

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

普通的對象枚舉適合小型項目或快速演示。

第二種選擇,如果你想保護枚舉對象不被意外覆蓋,則可以使用凍結的對象:

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

凍結的對象枚舉適合于中型或大型項目,你要確保枚舉不會被意外地改變。

第三種選擇是代理方法:

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'
})

代理枚舉適用于中型或大型項目,以更好地保護你的枚舉不被覆蓋或訪問不存在的命名常量。

代理的枚舉是我個人的偏好。

第四種選擇是使用基于類的枚舉,其中每個命名的常量都是類的實例,并作為類的靜態屬性被存儲:

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
  }
}

如果你喜歡類的話,基于類的枚舉是可行的。然而,基于類的枚舉比凍結的或代理的枚舉保護得更少。

你還知道哪些在JavaScript中創建枚舉的方法?

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

以上就是本文的全部內容,如果對你有所幫助,歡迎點贊、收藏、轉發~

參考資料

[1]

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

分享到:
標簽:JavaScript
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定