理解JavaScript中的位运算操作

4

我目前将数据以二进制形式存储在XML文档中,每个值为20位数字,代表一个布尔值。

<matrix> 

    <resource type="single"> 
        <map>10001010100011110000</map> 
        <name>Resource Title</name> 
        <url>http://www.yoursite.com</url> 
    </resource>

</matrix>

我正在使用jQuery解析这个文本,目前使用for循环和charAt()函数判断数值是否等于"1",以此来决定是否执行某些操作。

for (var i = 0; i < _mapLength; i++) {
    if (map.charAt(i) == "1") {
        //perform something here
    }
}

这个操作在一个运行速度相对较慢的循环中多次执行。有人告诉我应该使用位运算符来处理它,这样可以更快。

我的问题是:

能否给我提供一个如何做到这一点的示例?我已经尝试在线教程,但似乎完全不理解。(FYI: 我打算创建一个 Ruby 脚本,将我的二进制 0 和 1 转换为 XML 中的位。)

还是有人知道一个好的、简单的(甚至是简化版) 教程或其他东西,可以帮助我掌握这些位运算符的概念吗?


很遗憾,如果你必须为每个位执行操作,我认为你不太可能获得太多的性能提升。 - Ian Elliott
18
XML 中的二进制让我感到沮丧。 - GuiSim
你可以将那个20位二进制数以更高的进制存储为更紧凑的数字,并且如果需要,在JavaScript中仍然可以使用它进行布尔运算。 - Nosredna
4个回答

14

假设您最多拥有32位,您可以使用JavaScript内置的 parseInt() 函数将您的01字符串转换为整数,然后使用 & (与)运算符测试标志:

var flags = parseInt("10001010100011110000", 2); // base 2

if ( flags & 0x1 )
{
   // do something
}

...

另请参阅:如何检查我的字节标志位?

(该问题涉及在C语言中的使用,但同样适用于JS中相同的运算符)


1
是的,如果您想从右到左遍历所有位,可以重复使用"&1"并在每次检查后进行右移。 - Nosredna
1
一个更准确的说法是,“你可以使用JS中的parseInt()将字符串转换为Number”,因为程序员无法访问真正的整数类型。 - NullUserException
严格来说,即使在二进制表示中,它始终是整数。但是,无论您存储什么,数据类型都是浮点数。 - Shog9

3
非常谨慎。JavaScript没有整数——数字以64位浮点数存储。您应该获得高达52位的准确转换。如果您获得的标志超过了这个范围,那么当您的“数字”四舍五入到最近的可表示浮点数时,会发生糟糕的事情。(疼!)
此外,位操作不会提高性能,因为浮点数将被转换为整数进行测试,然后再转换回来。
如果您有多个要检查标志的地方,我建议在对象上设置标志,最好带有名称,如下所示:
var flags = {};
flags.use_apples = map.charAt(4);
flags.use_bananas = map.charAt(10);

然后您可以在循环内测试这些标志:

if(flags.use_apples) {
    do_apple_thing();
}

对象槽测试比位运算检查更快,因为Javascript没有针对位运算符进行优化。然而,如果您的循环速度较慢,我担心解码这些标志可能不是缓慢的原因。


不确定为什么有人说JS中的位运算性能很差。实际上并非如此。我已经测试过JavaScript的位运算速度,它们似乎足够快。任何转换都不会引起明显的延迟。最多只会有一点移位和掩码操作。 - Nosredna
1
-1 这并不完全正确,如果所涉及的数字无法转换为有符号32位整数,则无论如何都会发生错误,因为JS中的位运算是基于整数而非浮点数进行的。而关于性能的说法也没有根据。 - NullUserException

3

单个&符号(&,而不是&&)进行位比较。但首先需要使用parseInt()将字符串转换为数字。

var map = parseInt("10010", 2); // the 2 tells it to treat the string as binary

var maskForOperation1 = parseInt("10000", 2);
var maskForOperation2 = parseInt("01000", 2);
// ...

if (map & maskForOperation1) { Operation1(); }
if (map & maskForOperation2) { Operation2(); }
// ...

一个可能会使用 var maskForOperation1 = 0x1 << 4;,只需更改数字 4 即可获得其他掩码(例如,对于 maskForOperation2,它将是 3)。 - Felix Dombek

1

位运算符肯定会更快,但只是线性的,并且提升不会太多。你可能会节省几毫秒的时间(除非你在Javascript中处理大量数据,这很可能是个坏主意)。

你应该考虑对循环中的其他代码进行分析,看看哪些代码是最拖慢速度的。你还有哪些算法、数据结构和分配可以重构呢?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接