JavaScript實(shí)現(xiàn)枚舉的幾種方法總結(jié)
基于普通對(duì)象
我們來(lái)考慮一個(gè)場(chǎng)景:T恤的尺寸:Small、Medium、Large三種類(lèi)型,那么我們使用普通對(duì)象的方式來(lái)實(shí)現(xiàn),代碼如下:
const Sizes = {
Small: 'small',
Medium: 'medium',
Large: 'large',
}基于這種方式實(shí)現(xiàn)一個(gè)枚舉是非常簡(jiǎn)單的,而且也足夠的清晰,此時(shí)Sizes就是一個(gè)基于JavaScript普通對(duì)象的枚舉,它有3個(gè)命名常量:Size.Small、Size.Medium、Size.Large,我們可以通過(guò)Size.small來(lái)獲取對(duì)應(yīng)的枚舉值。
優(yōu)點(diǎn)
- 簡(jiǎn)單:使用這種方式實(shí)現(xiàn)枚舉是非常簡(jiǎn)單,只需要定義一個(gè)帶有鍵和值的對(duì)象即可
缺點(diǎn)
- 容易被外部修改
當(dāng)我們?cè)诰S護(hù)一個(gè)大型的代碼倉(cāng)庫(kù)時(shí),枚舉值可能會(huì)被意外更改,代碼如下:
const size1 = Sizes.Medium const size2 = Sizes.Medium = 'foo' // Changed! console.log(size1 === Sizes.Medium) // logs false
如代碼中所示,當(dāng)枚舉被修改后,之后的邏輯將會(huì)出現(xiàn)問(wèn)題,這也就是說(shuō),我們實(shí)現(xiàn)枚舉的時(shí)候需要考慮對(duì)象的值不能被修改。
基于Symbol
我們也可以這樣實(shí)現(xiàn)一個(gè)枚舉:
const Sizes = {
Small: Symbol('small'),
Medium: Symbol('medium'),
Large: Symbol('large'),
}
const mySize = Sizes.Large;
console.log(mySize === Sizes.Large); // true
console.log(mySize === Symbol('large')); // false使用這種方式實(shí)現(xiàn)的枚舉看上去和機(jī)遇普通對(duì)象的方式類(lèi)似,只是把對(duì)象中的值修改成了Symbol類(lèi)型,那么這樣和剛才的方式又有什么不同呢?
優(yōu)點(diǎn)
- 必須使用枚舉本身來(lái)進(jìn)行比較:也就是上面代碼中我們必須使用
Sizes.Large來(lái)比較,再重新創(chuàng)建一個(gè)Symbol('large')對(duì)比的話(huà)則不相等,而對(duì)比第一種方法,只要字符串相同即為相同,這種對(duì)比方式更加嚴(yán)格了
缺點(diǎn)
- 不能使用JSON.stringify,使用
JSON.stringify會(huì)將Symbol轉(zhuǎn)為undefined、null或直接跳過(guò),代碼如下:
console.log(JSON.stringify(Sizes.Small)); // undefined
console.log(JSON.stringify([Sizes.Small])); // [null]
console.log(JSON.stringify({ size: Sizes.Small })) // {}- 容易被外部修改,這點(diǎn)與第一種方案的情況類(lèi)似,接下來(lái),我們將介紹如何能夠保證枚舉值不會(huì)被修改的方案
基于Object.freeze
使用Object.freeze可以使一個(gè)對(duì)象被凍結(jié):被凍結(jié)的對(duì)象不能再被更改:不能添加新的屬性,不能移除現(xiàn)有的屬性,不能更改它們的可枚舉性、可配置性、可寫(xiě)性或值,對(duì)象的原型也不能被重新指定。我們使用這種實(shí)現(xiàn)枚舉,代碼如下:
const Sizes = Object.freeze({
Small: 'small',
Medium: 'medium',
Large: 'large',
})
const mySize = Sizes.Large;
Sizes.Large = '111'
console.log(mySize === Sizes.Large) // true使用這種方式,就算去修改枚舉值也是無(wú)效的,如果在嚴(yán)格模式下,這種賦值的情況還會(huì)拋出錯(cuò)誤。
優(yōu)點(diǎn)
- 有效防止枚舉值被修改
缺點(diǎn)
- 當(dāng)拼寫(xiě)錯(cuò)誤時(shí),會(huì)直接返回
undefined,比如我們直接獲取Sizes.a,此時(shí)會(huì)返回undefined,這個(gè)問(wèn)題在前面的幾個(gè)方案中也是同樣的,我們?cè)陂_(kāi)發(fā)過(guò)程中,應(yīng)該是更希望拋出一個(gè)錯(cuò)誤,這樣在開(kāi)發(fā)階段更直接的發(fā)現(xiàn)問(wèn)題所在。于是,就有了下面一種方案。
基于Proxy
使用Proxy用于創(chuàng)建一個(gè)對(duì)象的代理,從而實(shí)現(xiàn)基本操作的攔截和自定義,Proxy并不會(huì)改變?cè)紝?duì)象的結(jié)構(gòu),而且我們可以實(shí)現(xiàn)如下兩個(gè)需求:
- 訪(fǎng)問(wèn)不存在枚舉時(shí),拋出錯(cuò)誤
- 修改枚舉對(duì)象屬性時(shí),拋出錯(cuò)誤
這樣,就可以同時(shí)滿(mǎn)足我們前面幾種方案遇到的問(wèn)題了,接下來(lái),我們封裝一個(gè)函數(shù),代碼如下:
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')
}
})
}這個(gè)函數(shù)中,我們傳入一個(gè)初始枚舉對(duì)象,當(dāng)我們?cè)L問(wèn)某個(gè)屬性的時(shí)候,如果沒(méi)有將會(huì)拋出錯(cuò)誤"${name}" value does not exist in the enum,當(dāng)我們修改值的時(shí)候,也會(huì)拋出一個(gè)錯(cuò)誤Cannot add a new value to the enum接下來(lái),我們使用這個(gè)函數(shù)包裝一下Sizes,代碼如下:
const Sizes = Enum({
Small: 'small',
Medium: 'medium',
Large: 'large',
})
const mySize = Sizes.Large; // large
Sizes.Small = '1' // 拋出錯(cuò)誤: Cannot add a new value to the enum
console.log(Sizes.a) // 拋出錯(cuò)誤:"a" value does not exist in the enum優(yōu)點(diǎn)
- 枚舉值防止修改
- 訪(fǎng)問(wèn)不存在的枚舉時(shí)會(huì)拋出錯(cuò)誤
缺點(diǎn)
- 相對(duì)復(fù)雜,必須導(dǎo)入
Enum函數(shù)
基于Class
另一個(gè)方法是基于JavaScript中的Class類(lèi)實(shí)現(xiàn)的,這個(gè)類(lèi)中包含一組靜態(tài)的字段,而每一個(gè)對(duì)應(yīng)的值本身又是這個(gè)實(shí)例,代碼如下:
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;
}
}每一個(gè)枚舉值都是一個(gè)Sizes實(shí)例,內(nèi)部有個(gè)私有屬性#value, 用來(lái)表示枚舉的原始值。我們舉幾個(gè)例子來(lái)看下使用這種方式實(shí)現(xiàn)的具有哪些特性:
const mySize = Sizes.Large;
console.log('mySize', Sizes.Large); // Sizes {}
console.log(mySize === Sizes.Large); // true
console.log(mySize === new Sizes('large')) // false
console.log('mySize string', Sizes.toString()) // large
console.log(mySize instanceof Sizes) // true優(yōu)點(diǎn)
- 可以通過(guò)
instanceof來(lái)判斷是否是枚舉:上面例子中我們可以判斷出來(lái)mySize是一個(gè)枚舉 - 這種方式枚舉的對(duì)比是基于實(shí)例的:上面例子中
mySize === new Sizes('large'),即使是相同的#value,也是不同的實(shí)例
缺點(diǎn)
- 枚舉值可能會(huì)被意外修改
- 訪(fǎng)問(wèn)不存在的枚舉時(shí)不會(huì)拋出錯(cuò)誤
總結(jié)
上面我們介紹了幾種在JavaScript中實(shí)現(xiàn)枚舉的方式,每種方式都有各自的優(yōu)缺點(diǎn),相比之下,我認(rèn)為:
- Proxy方式更為靈活,可以按照自己的需求進(jìn)行更多的定制化;
- 如果枚舉值用的較多,且項(xiàng)目較大,選擇Object.freeze方式,防止枚舉值被意外修改;
- 如果您遇到的情況相對(duì)簡(jiǎn)單,使用基于普通對(duì)象的方式;
總之,我們最終的實(shí)現(xiàn)要盡量的簡(jiǎn)單,不要過(guò)度設(shè)計(jì),按照具體情況選擇合適的方式。
相關(guān)文章
JS判斷是否為JSON對(duì)象及是否存在某字段的方法(推薦)
下面小編就為大家?guī)?lái)一篇JS判斷是否為JSON對(duì)象及是否存在某字段的方法(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11
簡(jiǎn)單實(shí)現(xiàn)js點(diǎn)擊展開(kāi)二級(jí)菜單功能
這篇文章主要教大家簡(jiǎn)單實(shí)現(xiàn)js點(diǎn)擊展開(kāi)二級(jí)菜單功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05
微信小程序?qū)崿F(xiàn)圖片滾動(dòng)效果示例
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)圖片滾動(dòng)效果,結(jié)合實(shí)例形式分析了微信小程序基于swiper組件的圖片滾動(dòng)效果相關(guān)實(shí)現(xiàn)技巧與操作注意事項(xiàng),需要的朋友可以參考下2018-12-12
javascript動(dòng)態(tài)的改變IFrame的高度實(shí)現(xiàn)自動(dòng)伸展
動(dòng)態(tài)的改變IFrame的高度,實(shí)現(xiàn)IFrame自動(dòng)伸展,父頁(yè)面也自動(dòng)神縮,實(shí)現(xiàn)原理很簡(jiǎn)單就是在IFrame子頁(yè)面一加載的時(shí)候,調(diào)用父IFrame對(duì)象,改變其高度,感興趣的朋友可以了解下2013-10-10
JavaScript實(shí)現(xiàn)與web通信的方法詳解
這篇文章主要介紹了JavaScript實(shí)現(xiàn)與web通信的方法詳解,文章通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
JS+Ajax+Jquery實(shí)現(xiàn)頁(yè)面無(wú)刷新分頁(yè)以及分組 超強(qiáng)的實(shí)現(xiàn)
JS+Ajax+Jquery實(shí)現(xiàn)頁(yè)面無(wú)刷新分頁(yè)以及分組 超強(qiáng)的實(shí)現(xiàn) 加上你的CSS完全可以與EXT媲美哦2009-08-08
js實(shí)現(xiàn)簡(jiǎn)單排列組合的方法
這篇文章主要介紹了js實(shí)現(xiàn)簡(jiǎn)單排列組合的方法,可實(shí)現(xiàn)數(shù)學(xué)上排列組合算法功能,涉及JavaScript數(shù)組與字符串操作技巧,需要的朋友可以參考下2016-01-01
bootstrap datepicker限定可選時(shí)間范圍實(shí)現(xiàn)方法
這篇文章主要介紹了bootstrap datepicker限定可選時(shí)間范圍的實(shí)現(xiàn)方法,本文涉及到相關(guān)知識(shí)點(diǎn),通過(guò)實(shí)例給大家介紹的非常詳細(xì),需要的朋友可以參考下2016-09-09

