Bitmask (中文可以叫掩码)是一种通过按位运算来使用一个数值来表示多个二进制位的方式,现在已经不提倡使用了,但是老的 API 以及早期的编程语言、操作系统等提供的 API 中还是有使用的。
Bitmask 主要用于提供多个开关选项的 API 中,但是因为其语义不明确且传参方式需要用到按位或运算符(|
),所以现在大都是直接使用一个对象来传参。
不使用 Bitmask 的 API 设计,举个例子:
button.addEventListener('click', eventHandler, { capture: true, once: false, passive: true, })
|
注册事件监听时有三个可选项,现在都是使用这种键值对方式来开关选项的,语义很明确,如果涉及到条件判断等逻辑也很容易组织代码。如果使用 Bitmask 的方式来传参,可能会变成这个样子:
const EVENT_LISTENER_OPTION_CAPTURE = 0b001 const EVENT_LISTENER_OPTION_ONCE = 0b010 const EVENT_LISTENER_OPTION_PASSIVE = 0b100
button.addEventListener('click', eventHandler, EVENT_LISTENER_OPTION_CAPTURE)
button.addEventListener('click', eventHandler, EVENT_LISTENER_OPTION_CAPTURE | EVENT_LISTENER_OPTION_ONCE)
button.addEventListener( 'click', eventHandler, EVENT_LISTENER_OPTION_CAPTURE | EVENT_LISTENER_OPTION_ONCE | EVENT_LISTENER_OPTION_PASSIVE )
|
可以看到,这样传参很啰嗦,而且如果想通过代码判断逻辑来开关特定的选项也会很难写,因此 Bitmask 这种方式已经不再提倡了。
目前 WebAPI 中还是存在一些使用 Bitmask 的 API 的,例如 document.createNodeIterator(…):
const NodeFilter = { SHOW_ALL: 0xffffffff, SHOW_ELEMENT: 1 << 0, SHOW_ATTRIBUTE: 1 << 1, SHOW_TEXT: 1 << 2, SHOW_CDATA_SECTION: 1 << 3, SHOW_ENTITY_REFERENCE: 1 << 4, SHOW_ENTITY: 1 << 5, SHOW_PROCESSING_INSTRUCTION: 1 << 6, SHOW_COMMENT: 1 << 7, SHOW_DOCUMENT: 1 << 8, SHOW_DOCUMENT_TYPE: 1 << 9, SHOW_DOCUMENT_FRAGMENT: 1 << 10, SHOW_NOTATION: 1 << 11, }
document.createNodeIterator(document.body, NodeFilter.SHOW_ELEMENT)
document.createNodeIterator(document.body, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT)
|
因为 document.createNodeIterator
有多达12种的筛选模式,使用时有可能需要同时指定多种筛选模式,所以此 API 设计成使用 Bitmask 来传参;为了明确参数的语义,DOM 专门定义了 NodeFilter
对象来给这些选项赋予变量名。
上面的 document.createNodeIterator
是将 Bitmask 形式的值作为参数来使用的。WebAPI 中还有一些地方使用 Bitmask 形式作为返回值的,例如 mousedown
事件:
div.addEventListener('mousedown', e => { console.log(e.buttons) })
|
Node.js 中同样有 API 应用到了 Bitmask。举例:
const fs = require('fs')
fs.access('package.json', fs.constants.F_OK, err => { })
fs.access('package.json', fs.constants.R_OK, err => { })
fs.access('package.json', fs.constants.R_OK | fs.constants.W_OK, err => { })
|
如果想自行设计一个 Bitmask 参数的 API,可以使用以下方式来接收参数:
function testBitmask(bitmask) { if (bitmask & 0b001) { } if (bitmask & 0b010) { } if (bitmask & (0b001 | 0b010)) { } if (bitmask & 0b011) { } if ((bitmask & 0b001) && (bitmask & 0b010)) { } if ((bitmask & 0b011) === 0b011) { } }
testBitmask(-1)
testBitmask(0b001)
testBitmask(0b001 | 0b100) testBitmask(0b101)
|
调试的时候,可以通过 .toString(2)
来得到数值的二进制表示。
如果想熟练的使用 Bitmask,必须了解位运算。这里给出常用的位运算方法:
let bitmask = 0
bitmask |= 1 << 3
bitmask &= ~(1 << 3)
bitmask ^= 1 << 3
|