系统中常常会存在大量的状态信息,特别是0-1值信息,某个条件是否达成,某个功能是否存在,某个操作是否成功等等,通常的做法是将各种状态条件编号,利用boolean数组来表示。
//0表示a功能,1表示b功能 //a功能生效 status[0]=true; //b功能生效 status[1]=false;
由于大多数语言实际上是将boolean类型等同于整数类型,javascript也不例外,而其他语言和javascript不同的是:javascript目前必须运行在浏览器中,在特定条件下对于存储要求更加严格(例如cookie的4k限制),这时就要使用不常用的位操作来挖掘每一位存储的潜力。
将开关状态使用 0-1 二进制位表示,即将多个状态压缩到一个整数里面存储,一般一个整数有32位(javascript存在差异,尚不确定),则理论上节省了8倍的空间,代价则是增加了存取的运算。
1.存储数组
使用整数数组来拼成位集合
var store = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
2.设置状态
根据开关状态序号,定位到整数数组中的某个整数,设置特定的某个位为1
var index = Math.floor(num / 32), index2 = num % 32, mask = Math.pow(2, index2); store[index] |= mask;
3.清除状态
根据开关状态序号,定位到整数数组中的某个整数,设置特定的某个位为0
var index = Math.floor(num / 32), all_one = (Math.pow(2, 32) - 1), index2 = num % 32, mask = all_one ^ Math.pow(2, index2); store[index] &= mask;
4.存储
最后存储到cookie时都要转换成字符串的格式,这里我们有三个选项
1.将 store 的每个 int,直接 toString() 转换成 10 进制字符串
2.将 store 的每个 int,toString(16) 转换成 16 进制字符串
但是这两种都没有充分利用一个字符字节的完全存储空间,即使16进制下4位就已经占据了一个字符字节的空间。
3.更优的解即是:对每8位,通过 String.fromCharCode 转为一个字节,最终存储到cookie。
5.解码
根据cookie中的字节,通过string.charCodeAt 即可获得该字节代表的数字,进一步通过位运算即可获取各个开关状态
var v = S.Cookie.get("test"); for (var i = 0; i < v.length; i++) { //解码 var n = v[i].charCodeAt(0); }
简单的可以通过对状态组数字的每一位进行检测是否0,1来得知状态设置为开的序号 :复杂度为 O(n) ,一般 n=32
//slower function getOnesSlow(n,base,re) { base=base||0; var mask=1; for(var i=0;i<32;i++){ if(n&mask) re.push(i); mask=mask<<1; } }
其实存在更精巧的算法,主要是利用了负数的二进制其实就是由绝对值二进制取反后加一形成的,当一个数字和其负数进行按位与时,返回结果就是除了原数的最低位1保留外,其他都是0,例如
1010 对应负数为 111...0101,加1为 1111...0110
1111...0110 & 1010 = 10
结果 1 的位顺序数即为原数字 1010 的最低位 1的位顺序数
为了能根据 10..0 快速得知1所在的位顺序数,可以预先建立索引而不用循环计数:
var bton={}; var base=1; for(var i=0;i<32;i++){ bton[base]=i; base=base<<1; }
那么现在就可以快速得到数字包含的1所在的位数数组:
//fastest one : function getOnes(n,base,re){ base=base||0; while(n){ var mask=n&-n; re.push(bton[mask]+base); n &= ~mask; } }
最快0次循环,即 n=0,最慢32次循环,即 n=1111...1(javascript位操作对象为32位整数),平均为 16 次循环,速度相比简单的逐位检测算法提高了一倍。
6.问题
在 cookie 中,每个字节并不能完整的表示8bit的信息量,因为ASCII中存在一些区间是特殊作用的字符(比如头32个)。作为HTTP头的一部分,理论上是不能使用这些字符的。
参考Uuencode算法(http://en.wikipedia.org/wiki/Uuencoding ),一般每个ANSI字符可以编码6bit的信息量。(感谢提醒)
延伸阅读
javascript 在位操作领域也开始显露头角,国外有参照java流读取设计的 base64 解码器,甚至 deflate 解码器 ,从而产生了直接使用div构建png 图片的演示,非常不错,还有:
Embedding Base64 Image Data into a Webpage
Use Javascript to Take a Screenshot of a Flash Movie
Base64 Encoded Images for Internet Explorer
Parsing Base64 Encoded Binary PNG Images in JavaScript