JavaScript 高效枚举:利用位运算优化性能

作者:缓存笔记时间:2026-10-25 13:08:46浏览:578

本文介绍一种基于位运算的高效 JavaScript 枚举实现方式。通过左移运算符生成 2 的 N 次方值,利用按位或进行状态合并,并通过按位与判断成员存在性。该方法源自 Vue 源码,相比传统数组或对象存储,能显著提升 CPU 执行效率并节省内存空间。

前言

假设有一个实际需求,需要统计员工掌握的技术栈,当前需要标记的技术包括 CSS、JavaScript、HTML 和 WebGL。

传统的实现方式可能如下所示:

const SKILLS = {
 CSS: 1 ,
 JS: 2,
 HTML: 3,
 WEB_GL: 4
}

以往我们通常采用这种方式,但在阅读 Vue 源码时,发现了一种利用位运算高效处理枚举的技巧,特此分享。

定义枚举

我们可以采用如下方式重新定义上述枚举:

const SKILLS = {
 CSS: 1 ,
 JS: 1 << 1,
 HTML: 1 << 2,
 WEB_GL: 1 << 3
}

<< 是什么?

左移运算符 (<<) 的作用是将第一个操作数的二进制位向左移动指定的位数。向左移出的多余位会被丢弃,右侧空出的位由 0 填充。

例如:

  • 二进制数 1 表示为 0000 0001,左移一位后变为 0000 0010,即十进制的 2。
  • 若左移两位,结果变为 0000 0100,即十进制的 4。
  • 若左移三位,结果变为 0000 1000,即十进制的 8。
  • 若左移 N 位,结果即为十进制的 2^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)

前言

温馨提示:| 是按位或运算符,它在两个操作数对应位均为 1 的位置返回 1。

cons
t a = 5;        // 00000000000000000000000000000101
const b = 3;        // 00000000000000000000000000000011
console.log(a | b); // 00000000000000000000000000000111
// expected output: 7

如何理解这段代码?

在 JavaScript 中,整数以 4 个字节(32 位)存储,其中第一位表示正负号,剩余 31 位表示数值大小。

当我们用二进制表示 1 和 1 << 2 时,它们的形式如下:

前言

我们定义的枚举变量本质上只有一个二进制位为 1,且该 1 的位置各不相同。

当我们需要向技能集合中添加新的枚举选项时,使用 skills | skill。假设当前需要添加的技能是 SKILLS.CSS,执行过程如下:

前言

观察可知,在 skills 变量中,SKILLS.CSS 对应的位置会变为 1。反之,我们可以通过检查 skills & SKILLS.CSS 的结果是否不为 0,来判断 skills 中是否包含 SKILLS.CSS。此外,该技巧的一个局限性是枚举项的数量不能超过 31 个。

我们为什么要使用这个技巧?

原因很简单:这种代码执行效率更高。CPU 内置了直接处理位操作的指令,因此运算速度更快。

我们可以进行性能测试。如果不使用位运算,而是采用传统方法(如数组或映射)来实现,代码如下。

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 上的性能测试结果:

前言

结果显示,使用按位枚举的性能明显更高。

学习Vue源码

这一技巧源自对 Vue 源代码的学习。

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
}

到此这篇关于JavaScript 编写枚举的最有效方法分享的文章就介绍到这了,更多相关JavaScript 枚举编写内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

作者最新文章