Marketechlabo

JavaScriptでフラグをビットで管理する~複数の状態フラグを少ない容量で管理

概要

状態を管理する、その状態に応じて条件分岐するやり方。

例: ランディングページで「30秒以上滞在」「スクロール深度50%到達」「フォーム入力開始」を検知し、3つとも満たしたユーザーにのみリターゲティング用のカスタムオーディエンスを送る。あるいは、ECサイトで「トップ閲覧」「カテゴリ閲覧」「詳細閲覧」を1つの数値で持ち、商品詳細まで到達したユーザーにのみ関連商品リコメンドのタグを発火させる。 個別の条件を満たしたときにフラグを立て、その組み合わせを参照して処理を分岐させる。 単純に1フラグを1変数にすると変数の数が増える。 そこで1個の変数で複数の状態をまとめて管理する方法がビットによる管理である。 なおこの考え方はJavaScriptに限らずあらゆるプログラム言語で実装が可能。 管理するフラグが多く、メモリ消費や発生するパケットを小さくする必要があるときに重要となる。

ビットは0または1の2値を表す情報の単位 それを8個つなげたもの、

11001101

は8個の0/1を表す情報を保持している。 11001101の各桁は0か1しかないので2進数として見ることができる。 これを10進数に置き換えると205ということになる。 8ビットの変数1個(0255の整数)であれば8個のビット(0/1)が収まる。 つまり0255の整数で8種類の0/1の状態を表せる。 この最小の情報の単位であるビットに対する処理がビット処理である。

var STATE = {
    DISABLED: 1 << 0,
    HOVER: 1 << 1,
    ACTIVE: 1 << 2
};
javascript

1<<桁で、桁は0からスタート

まず状態を表す変数を宣言

var state;
javascript
state |= STATE.DISABLED;
javascript

フラグDISABLEDHOVERを同時に立てる(一度に代入する)

state |= STATE.DISABLED | STATE.HOVER;
javascript
state = STATE.DISABLED | STATE.HOVER | STATE.ACTIVE;
javascript
state &= ~STATE.DISABLED;
javascript

フラグを下げる場合は「&」にするのと否定の「~」を付ける フラグDISABLEDHOVERを同時に下げる(一度に代入する)

state &= ~(STATE.DISABLED | STATE.HOVER);
javascript
state ^= STATE.DISABLED;
javascript

stateの中でDISABLEDに対応するビットの値を抜き出す(これが0でなければDISABLEDのフラグが立っていることになる)

state & STATE.DISABLED
javascript

stateの中でDISABLEDHOVERに対応する部分を抜き出す

state & (STATE.DISABLED | STATE.HOVER)
javascript
  • これが0でなければDISABLEDまたはHOVERのフラグが立っていることになる
  • これが(STATE.DISABLED | STATE.HOVER)と等しい場合、DISABLEDHOVERの両フラグが立っていることになる

フラグの取り出しは条件判定で使うことになる。

やりたいこと演算方法演算子変更したいビットそのままにしたいビット
ビットをすべて反転させたいNOT~すべて変更される指定不可
一部のビットを反転させたいXOR^反転させたいビット: 10
一部のビットをONにしたいOR``ONにしたいビット: 1
一部のビットをOFFにしたいAND&OFFにしたいビット: 01
if (state & STATE.DISABLED) { 処理 }
if (state & (STATE.DISABLED | STATE.HOVER)) { 処理 }
!!(state & STATE.DISABLED) && 処理
javascript

UIの状態管理の例。ボタンが無効でなく、かつホバー中の場合にのみクリック処理を実行する。

var STATE = {
    DISABLED: 1 << 0,
    HOVER: 1 << 1,
    ACTIVE: 1 << 2
};
var btnState = parseInt(getButtonState(), 10) || 0;
// ホバー中で、かつ無効でない場合にクリック処理を許可
if ((btnState & STATE.HOVER) && !(btnState & STATE.DISABLED)) {
    console.log("Click allowed");
}
// 複数フラグの同時判定(ACTIVE と HOVER の両方がある場合)
if ((btnState & (STATE.ACTIVE | STATE.HOVER)) === (STATE.ACTIVE | STATE.HOVER)) {
    console.log("Active and hovering");
}
javascript

GTMのカスタムHTMLタグなど、ES5環境で動かす場合は上記のようにvarで宣言する。