web-dev-qa-db-fra.com

Où devrais-je utiliser un opérateur de bitwise en JavaScript?

J'ai lu "quels sont les opérateurs de bitwise?" , je sais donc ce queopérateurs au niveau du bit sont mais je ne comprends toujours pas comment les utiliser. Quelqu'un peut-il offrir des exemples concrets de situations dans lesquelles un opérateur bitwise serait utile en JavaScript?

Merci.

Modifier:

En fouillant simplement dans la source jQuery, j'ai trouvé quelques endroits où les opérateurs au niveau du bit sont utilisés, par exemple: (uniquement l'opérateur &)

// 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;
65
James

Exemple: 

Analyse la valeur hexadécimale pour obtenir les valeurs de couleur RVB.

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  
64
Mark

I utilise fortement des opérateurs au niveau du bit pour les conversions numériques dans les scripts de production, car ils sont parfois beaucoup plus rapides que leurs équivalents Math ou parseInt.

Le prix que je dois payer est lisibilité du code. Donc j’utilise habituellement Math en développement et au niveau du bit en production.

Vous pouvez trouver quelques astuces de performance sur jsperf.com .

Comme vous pouvez le constater, les navigateurs n'optimisent pas Math.ceil et parseInt pendant des années, aussi je prédis que bitwise sera un moyen plus rapide et plus rapide de faire des choses également dans l'avenir

Quelques lectures supplémentaires sur SO ...


Bonus: cheat sheet pour | 0: un moyen facile et rapide de convertir n'importe quoi en entier:

( 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;

et de la magie pour moi: 

3 | '0px' === 3;
39
Dan

En JavaScript, vous pouvez utiliser une double négation binaire (~~n) pour remplacer Math.floor(n) (si n est un nombre positif) ou parseInt(n, 10) (même si n est négatif). n|n et n&n donnent toujours les mêmes résultats que ~~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

Une seule négation au niveau du bit (~) calcule -(parseInt(n, 10) + 1); deux négations au niveau du bit renverront donc -(-(parseInt(n, 10) + 1) + 1).

Il convient de noter que parmi ces trois alternatives, n|n semble être le plus rapide.

Mise à jour: Des repères plus précis ici: http://jsperf.com/rounding-numbers-down

(Telle que publiée sur Fonction de langue la plus étrange )

24
Mathias Bynens

Une vraie vieexemple :

^ bitwise XOR en tant que I/O bascule

Utilisé comme value ^= 1 changera à chaque appel la value en 0, 1, 0, 1 ... 

function toggle(evt) {
  evt.target.IO ^= 1;                                    // Bitwise XOR as 1/0 toggler
  evt.target.textContent = evt.target.IO ? "ON" : "OFF"; // Unleash your ideas
}

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

18
Roko C. Buljan

Compte tenu des progrès réalisés par Javascript (en particulier avec nodejs qui permet la programmation côté serveur avec js), le code dans JS est de plus en plus complexe. Voici quelques cas où j'ai utilisé des opérateurs au niveau des bits: 

  • Opérations d'adresse IP: 

    //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: c'est du code C, mais JS est presque identique 

  • Les algorithmes CRC les utilisent beaucoup

Découvrez l'entrée de wikipedia sur ce

  • Opérations de résolution d'écran
16
Bogdan Gavril MSFT

Pour dire si un nombre est impair:

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

Plus rapide que le module - utilisez où performance vraiment compte!

12
danwellman

Quelques autres exemples d'utilisation pas à pas et à double pas:

Fonctionnement au sol

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

Vérifie si indexOf a retourné -1 ou non

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

Vous pouvez les utiliser pour renverser une valeur booléenne:

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

C'est un peu idiot et pour la plupart des opérateurs au niveau des bits n'ont pas beaucoup d'applications en Javascript.

10
Andrew Hare
var arr = ['abc', 'xyz']

Agacé d'écrire 

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

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

vérifier si quelque chose est à l'intérieur d'un tableau?

Vous pouvez utiliser l'opérateur binaire ~ comme suit:

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

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

Masques de bits.

Utilisé intensivement, par exemple, dans les événements JS.

4
Yuval Adam

Je l'ai utilisé une fois pour un widget permissions . Les autorisations de fichiers sous Unix sont un masque de bits. Par conséquent, pour les analyser, vous devez utiliser des opérations sur les bits.

3
troelskn

Je les utilise pour aplatir trois nombres en 1 afin de stocker des tableaux multidimensionnels dans un Uint16Array . Voici un extrait du jeu Voxel que je développe:

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]);

2
Bardi Harborow

Cette réponse contient des explications sur La réponse de Mark .

En lisant ces explications et en lançant l'extrait de code, une idée peut être gagnée.

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) */

1
efkan

Ils semblent être très utiles lorsque vous travaillez avec des valeurs hexadécimales et des bits. Depuis 4 bits peuvent représenter 0 à F.

1111 = F 1111 1111 = FF.

0
eternauta

Exemple d'utilisation de Node.js

En supposant que vous ayez un fichier (appelé multiply.js) avec ce contenu, vous pouvez exécuter 

`node multiply <number> <number>`

et obtenez une sortie compatible avec l'utilisation de l'opérateur de multiplication sur les deux mêmes nombres. Le décalage de bits dans la fonction Mulitply est un exemple de la manière de prendre le masque de bits représentant un nombre et de l'utiliser pour retourner des bits d'un autre nombre pour des opérations rapides.

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);
0
mysterlune