在 JavaScript 中哪里可以使用位运算符?

我读过 什么是位运算符,所以我知道 什么 按位运算符是,但我仍然不清楚如何使用它们。有人能提供一些真实的例子来说明在 JavaScript 中哪些位运算符是有用的吗?

谢谢。

编辑:

只要深入研究 JQuery source < a href = “ http://ajax.googleapis.com/ajax/libs/jQuery/1.3.2/jQuery.js”rel = “ norefrer”> jQuery source ,我就发现了一些使用位运算符的地方,例如: (只有 & 运算符)

// Line 2756:
event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));


// Line 2101
var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
34721 次浏览

Given the advances Javascript is making (especially with nodejs that allows server side programming with js), there is more and more complex code in JS. Here are a couple of instances where I have used bitwise operators:

  • IP address operations:

    //computes the broadcast address based on the mask and a host address
    broadcast = (ip & mask) | (mask ^ 0xFFFFFFFF)
    
    
    
    
    //converts a number to an ip adress
    sprintf(ip, "%i.%i.%i.%i", ((ip_int >> 24) & 0x000000FF),
    ((ip_int >> 16) & 0x000000FF),
    ((ip_int >>  8) & 0x000000FF),
    ( ip_int        & 0x000000FF));
    

Note: this is C code, but JS is almost identical

  • CRC algorithms uses them a lot

Check out the wikipedia entry on this

  • Screen resolution operations

You can use them for flipping a boolean value:

var foo = 1;
var bar = 0;
alert(foo ^= 1);
alert(bar ^= 1);

This is a bit silly though and for the most part bitwise operators do not have many applications in Javascript.

Bitmasks.

Used extensively, for example, in JS events.

Example:

Parses hexadecimal value to get RGB color values.

var hex = 'ffaadd';
var rgb = parseInt(hex, 16); // rgb is 16755421




var red   = (rgb >> 16) & 0xFF; // returns 255
var green = (rgb >> 8) & 0xFF;  // 170
var blue  = rgb & 0xFF;     // 221

I've used it once for a permissions widget. File permissions in unix are a bitmask, so to parse it, you need to use bit operations.

In JavaScript, you can use a double bitwise negation (~~n) as a replacement for Math.floor(n) (if n is a positive number) or parseInt(n, 10) (even if n is negative). n|n and n&n always yield the same results as ~~n.

var n = Math.PI;
n; // 3.141592653589793
Math.floor(n); // 3
parseInt(n, 10); // 3
~~n; // 3
n|n; // 3
n&n; // 3


// ~~n works as a replacement for parseInt() with negative numbers…
~~(-n); // -3
(-n)|(-n); // -3
(-n)&(-n); // -3
parseInt(-n, 10); // -3
// …although it doesn’t replace Math.floor() for negative numbers
Math.floor(-n); // -4

A single bitwise negation (~) calculates -(parseInt(n, 10) + 1), so two bitwise negations will return -(-(parseInt(n, 10) + 1) + 1).

It should be noted that of these three alternatives, n|n appears to be the fastest.

Update: More accurate benchmarks here: http://jsperf.com/rounding-numbers-down

(As posted on Strangest language feature)

Few other examples of how to use bitwise not and double bitwise not:

Floor operation

~~2.5    // 2
~~2.1    // 2
~~(-2.5) // -2

Check whether indexOf returned -1 or not

var foo = 'abc';
!~foo.indexOf('bar'); // true

I heavily use bitwise operators for numerical convertions in production scripts, because sometimes they're much faster than their Math or parseInt equivalents.

The price I have to pay is code readability. So I usualy use Math in development and bitwise in production.

You can find some performance tricks on jsperf.com.

As you can see, browsers don't optimize Math.ceil and parseInt for years, so I predict bitwise will be faster and shorter way to do things in furure as well.

Some further reading on SO...


Bonus: cheat sheet for | 0 : an easy and fast way to convert anything to integer:

( 3|0 ) === 3;             // it does not change integers
( 3.3|0 ) === 3;           // it casts off the fractional part in fractionalal numbers
( 3.8|0 ) === 3;           // it does not round, but exactly casts off the fractional part
( -3.3|0 ) === -3;         // including negative fractional numbers
( -3.8|0 ) === -3;         // which have Math.floor(-3.3) == Math.floor(-3.8) == -4
( "3"|0 ) === 3;           // strings with numbers are typecast to integers
( "3.8"|0 ) === 3;         // during this the fractional part is cast off too
( "-3.8"|0 ) === -3;       // including negative fractional numbers
( NaN|0 ) === 0;           // NaN is typecast to 0
( Infinity|0 ) === 0;      // the typecast to 0 occurs with the Infinity
( -Infinity|0 ) === 0;     // and with -Infinity
( null|0 ) === 0;          // and with null,
( (void 0)|0 ) === 0;      // and with undefined
( []|0 ) === 0;            // and with an empty array
( [3]|0 ) === 3;           // but an array with one number is typecast to number
( [-3.8]|0 ) === -3;       // including the cast off of the fractional part
( [" -3.8 "]|0 ) === -3;   // including the typecast of strings to numbers
( [-3.8, 22]|0 ) === 0     // but an Array with several numbers is typecast to 0
( {}|0 ) === 0;                // an empty object is typecast to 0
( {'2':'3'}|0 ) === 0;         // or a not empty object
( (function(){})|0 ) === 0;    // an empty function is typecast to 0 too
( (function(){ return 3;})|0 ) === 0;

and some magic for me:

3 | '0px' === 3;

I just found this question trying to confirm if the bitwise AND operator also was & in Javascript.

Since you asked for an example:

if ($('input[id="user[privileges]"]').length > 0) {
$('#privileges button').each(function () {
if (parseInt($('input[id="user[privileges]"]').val()) & parseInt($(this).attr('value'))) {
$(this).button('toggle');
}
});
}

It populates the state of buttons with jQuery given a bitmask value of a hidden field:

  • none = 0
  • user = 1
  • administrator = 2
  • user + administrator = 3

They seem to be very useful when you work with hex values and bits. Since 4 bits can represent 0 to F.

1111 = F 1111 1111 = FF.

^ bitwise XOR as toggler

value ^= 1 will change on every call the value to 0, 1, 0, 1, ... , (it's basically similar to: boolVal != boolVal):

function toggle(evt) {
const EL = evt.currentTarget;
EL.isOn ^= 1; // Bitwise toggle
EL.textContent = EL.isOn ? "ON" : "OFF"; // Unleash your ideas
}


document.querySelectorAll("button").forEach( el =>
el.addEventListener("click", toggle)
);
<button>OFF</button>
<button>OFF</button>
<button>OFF</button>

To tell if a number is odd:

function isOdd(number) {
return !!(number & 1);
}


isOdd(1); // true, 1 is odd
isOdd(2); // false, 2 is not odd
isOdd(357); // true, 357 is odd

Faster than modulus - use where performance really counts!

I'm using them to flatten three numbers into 1 as a way of storing multidimensional arrays in a Uint16Array. Here is a snippet of a voxel game I'm developing:

function Chunk() {
this._blocks = new Uint16Array(32768);
this._networkUpdates = [];
}


Chunk.prototype.getBlock = function(x, y, z) {
return this._blocks[y + (x << 5) + (z << 10)];
};


Chunk.prototype.setBlock = function(x, y, z, value) {
this._blocks[y + (x << 5) + (z << 10)] = value;
this._networkUpdates.push(value + (y << 15) + (x << 20) + (z << 25));
};


Chunk.prototype.getUpdates = function() {
return this._networkUpdates;
};


Chunk.prototype.processUpdate = function(update) {
// this._blocks[Math.floor(update / 65536)] = update % 65536;
this._blocks[update >> 16] = update & 65535;
};


var chunk = new Chunk();
chunk.setBlock(10, 5, 4);
alert(chunk.getBlock(10, 5, 4));
alert(chunk.getUpdates()[0]);

Example using Node.js

Presuming you had a file (called multiply.js) with these contents, you could run

`node multiply <number> <number>`

and get an output consistent with using the multiplication operator on the same two numbers. The bit shifting going on in the Mulitply function is an example of how to take the bit mask representing one number and use it to flip bits in another number for fast operations.

var a, b, input = process.argv.slice(2);


var printUsage = function() {
console.log('USAGE:');
console.log('  node multiply <number> <number>');
}


if(input[0] === '--help') {+
printUsage();
process.exit(0);
}


if(input.length !== 2) {
printUsage();
process.exit(9);
}


if(isNaN(+input[0]) || isNaN(+input[1])) {
printUsage();
process.exit(9);
}


// Okay, safe to proceed


a = parseInt(input[0]),
b = parseInt(input[1]);


var Multiply = function(a,b) {
var x = a, y = b, z = 0;


while( x > 0 ) {
if(x % 2 === 1) {
z = z + y;
}
y = y << 1;
x = x >> 1;
}


return z;
}


var result = Multiply(a,b);


console.log(result);
var arr = ['abc', 'xyz']

Annoyed to write

if (arr.indexOf('abc') > -1) {
// 'abc' is in arr
}


if (arr.indexOf('def') === -1) {
// 'def' is not in arr
}

to check if something is inside an array?

You can use the bitwise operator ~ like so:

if (~arr.indexOf('abc')) {
// 'abc' is in arr
}


if (! ~arr.indexOf('def')) {
// 'def' is not in arr
}

This answer contains explanations of Mark's answer.

By reading these explanations and running the code snippet an idea can be gained.

var hex = 'ffaadd';
var rgb = parseInt(hex, 16); // rgb value is 16755421 in decimal = 111111111010101011011101 in binary = total 24 bits




var red   = (rgb >> 16) & 0xFF; // returns 255
var green = (rgb >> 8) & 0xFF;  // returns 170
var blue  = rgb & 0xFF;         // returns 221


// HOW IS IT


// There are two bitwise operation as named SHIFTING and AND operations.
// SHIFTING is an operation the bits are shifted toward given direction by adding 0 (zero) bit for vacated bit fields.
// AND is an operation which is the same with multiplying in Math. For instance, if 9th bit of the given first bit-set is 0
// and 9th bit of the given second bit-set is 1, the new value will be 0 because of 0 x 1 = 0 in math.


// 0xFF (000000000000000011111111 in binary) - used for to evaluate only last 8 bits of a given another bit-set by performing bitwise AND (&) operation.
// The count of bits is 24 and the first 16 bits of 0xFF value consist of zero (0) value. Rest of bit-set consists of one (1) value.
console.log("0xFF \t\t\t\t: ", 0xFF)




// 111111111010101011011101 -> bits of rgb variable
// 000000000000000011111111 -> 255 after (rgb >> 16) shifting operation
// 000000000000000011111111 -> 255 complement (changes the first 16 bits and does nothing for the last 8 bits)
// 000000000000000011111111 -> result bits after performing bitwise & operation
console.log("Red - (rgb >> 16) & 0xFF \t: ", (rgb >> 16) & 0xFF) // used for to evaluate the first 8 bits


// 111111111010101011011101 -> bits of rgb variable
// 000000001111111110101010 -> 65450 -> 'ffaa'
// 000000000000000011111111 -> 255 complement (changes the first 16 bits and does nothing for the last 8 bits)
// 000000000000000010101010 -> result bits after performing bitwise & operation
// calculation -> 000000001111111110101010 & 000000000000000011111111 = 000000000000000010101010 = 170 in decimal = 'aa' in hex-decimal
console.log("Green - (rgb >> 8) & 0xFF \t: ", (rgb >> 8) & 0xFF) // used for to evaluate the middle 8 bits


// 111111111010101011011101 -> 'ffaadd'
// 000000000000000011111111 -> 255 complement (changes the first 16 bits and does nothing for the last 8 bits)
// 000000000000000011011101 -> result bits after performing bitwise & operation
// calculation -> 111111111010101011011101 & 000000000000000011111111 = 221 in decimal = 'dd' in hex-decimal
console.log("Blue - rgb & 0xFF \t\t: ", rgb & 0xFF) // // used for to evaluate the last 8 bits.


console.log("It means that `FFAADD` hex-decimal value specifies the same color with rgb(255, 170, 221)")


/* console.log(red)
console.log(green)
console.log(blue) */