用 JavaScript 編寫枚舉的最有效方法
假設(shè)有這樣一個場景,我們需要統(tǒng)計員工的技術(shù)棧,目前我們需要標(biāo)記的技術(shù)有 CSS、JavaScript、HTML、WebGL。
然后我可以這樣寫枚舉:
const SKILLS = {
CSS: 1 ,
JS: 2,
HTML: 3,
WEB_GL: 4
}
之前是這樣寫的,但是,最近看vue源碼的時候,發(fā)現(xiàn)了一個高效使用枚舉的技巧,在這里分享給大家。
定義枚舉
我們可以這樣寫上面的枚舉:
const SKILLS = {
CSS: 1 ,
JS: 1 << 1,
HTML: 1 << 2,
WEB_GL: 1 << 3
}
<< 是什么?
左移運(yùn)算符 (<<) 將第一個操作數(shù)左移指定位數(shù)。向左移動的多余位被丟棄。零位從右側(cè)移入。
例如:
- 二進(jìn)制的 1 是 0000 0001 ,左移一位是 0000 0010 ,即十進(jìn)制的 2 。
- 如果我們將其移動兩位,它將變?yōu)?0000 0100 ,即十進(jìn)制的 4。
- 如果我們將其移動三位,它將變?yōu)?0000 1000 ,即十進(jìn)制的 8。
- 如果我們將其移動 N 位,它將變?yōu)?2^Nin 十進(jìn)制。
用法
按照上面的方法定義好枚舉后,我們可以這樣使用:
const SKILLS = {
CSS: 1 ,
JS: 1 << 1,
HTML: 1 << 2,
WEB_GL: 1 << 3
}
// Use this value to store a user's tech-stack
let skills = 0
// add a skill for the user
function addSkill(skill) {
skills = skills | skill
}
addSkill(SKILLS.CSS)
addSkill(SKILLS.JS)
// If this value is not 0, it means that the user has mastered the tech
console.log('Does he know CSS', SKILLS.CSS & skills)
console.log('Does he know JavaScript', SKILLS.JS & skills)
console.log('Does he know Web GL', SKILLS.WEB_GL & skills)
溫馨提示:| 是按位或運(yùn)算符,它在每個操作數(shù)的對應(yīng)位為 1 的每個位位置返回 1。
cons
t a = 5; // 00000000000000000000000000000101
const b = 3; // 00000000000000000000000000000011
console.log(a | b); // 00000000000000000000000000000111
// expected output: 7
如何理解這段代碼?
在 JavaScript 中,整數(shù)存儲在 4 個字節(jié)中,即 32 位。第一個代表正負(fù),后面的31代表數(shù)字。
當(dāng)我們用二進(jìn)制表示 1 , 1 << 2 時,它們看起來像這樣:
我們定義的枚舉變量只有一個二進(jìn)制格式的1,并且占據(jù)不同的位置。
當(dāng)我們向技能添加枚舉選項時,我們使用skills | skill。假設(shè)現(xiàn)在我們需要添加的技能是SKILLS.CSS,那么在執(zhí)行過程中,就是:
我們可以發(fā)現(xiàn),在技能中,SKILLS.CSS對應(yīng)的位置會變成1。
反之,那么我們可以通過查看skills&SKILLS.CSS的結(jié)果是否為0來判斷技能中是否存在SKILLS.CSS。
順便說一句,這里我們也可以發(fā)現(xiàn)這個技巧有個缺點(diǎn),就是枚舉項不能超過 31 個。
我們?yōu)槭裁匆褂眠@個技巧?
答案很簡單,這樣的代碼運(yùn)行起來更高效。CPU中有直接對應(yīng)位操作的指令,因此效率更高。
我們也可以做一個性能測試。
如果我們不使用按位運(yùn)算,而是使用傳統(tǒng)的方法(數(shù)組或映射)來實(shí)現(xiàn),那么代碼如下。
Array 方法:
const SKILLS = {
CSS: 1 ,
JS: 1 << 1,
HTML: 1 << 2,
WEB_GL: 1 << 3
}
// Use an array to store the user's tech-stack
let skills = []
function addSkill(skill) {
if (!skills.includes(skill)) { // Avoid duplicate storage
skills.push(skill)
}
}
addSkill(SKILLS.CSS)
addSkill(SKILLS.JS)
skills.includes(SKILLS.CSS)
skills.includes(SKILLS.JS)
skills.includes(SKILLS.WEB_GL)
Map 方法:
const SKILLS = {
CSS: 1 ,
JS: 1 << 1,
HTML: 1 << 2,
WEB_GL: 1 << 3
}
// Use a map to store the user's tech-stack
let skills = {}
function addSkill(skill) {
if (!skills[skill]) {
skills[skill] = true
}
}
addSkill(SKILLS.CSS)
addSkill(SKILLS.JS)
skills[SKILLS.CSS]
skills[SKILLS.JS]
skills[SKILLS.WEB_GL]
這是 jsbench.me 的性能測試:
使用按位枚舉,性能明顯更高。
學(xué)習(xí)Vue源碼
我是從 Vue 源代碼中學(xué)到的。
export const enum ShapeFlags {
ELEMENT = 1,
FUNCTIONAL_COMPONENT = 1 << 1,
STATEFUL_COMPONENT = 1 << 2,
TEXT_CHILDREN = 1 << 3,
ARRAY_CHILDREN = 1 << 4,
SLOTS_CHILDREN = 1 << 5,
TELEPORT = 1 << 6,
SUSPENSE = 1 << 7,
COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8,
COMPONENT_KEPT_ALIVE = 1 << 9,
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}
地址:https://github1s.com/vuejs/core/blob/HEAD/packages/shared/src/shapeFlags.ts