JavaScript Operators
Last updated:
Comparison Operators
Equality
“Loose” Equality Operator (==)
true if the operands are equal after type conversion:
1 == 1 // true
1 == '1' // true
1 == true // true
0 == false // true
Using the “loose” equality operator can lead to unexpected results:
null == undefined // true
0 == '' // true
0 == [] // true
1 == ["1"] // true
1 == "hello" // true
1 == true // true
true == "hello" // false
Strict Equality Operator (===)
true if the operands are strictly equal with no type conversion:
1 === 1 // true
1 === '1' // false
null === undefined // false
recommended to use strict equality comparisons whenever possible.
Not Equal Operators
1 != "1" // false
1 !== "1" // true
1 != "true" // false
1 !== "true" // true
Other Comparison Operators
const x = 1
x < 1 // false
x <= 1 // true
x > 1 // false
x >= 1 // true
Logical Operators
AND Operator
let x = 5;
let y = 10;
if (x > 0 && y > 0) {
console.log("Both x and y are positive");
}
NB: uses short-circuit evaluation - the second operand will only be evaluated if the first operand is true. Can be useful in situations where the second operand could cause an error if it was evaluated when the first operand is false.
let x = null;
if (x !== null && x.length > 5) {
console.log("The string is long enough");
}
OR Operator
let age = 25;
if (age < 18 || age > 60) {
console.log("You are not eligible for this job");
}
NB: also uses short-circuit evaluation - the second operand will only be evaluated if the first operand is false. Can also be useful in situations where we want to provide a default value if a variable is undefined or null.
let x = null;
let y = x || "default value";
console.log(y); // prints: "default value"
NOT Operator
let loggedIn = false;
if (!loggedIn) {
console.log("Please login to continue");
}
Ternary Operator
condition ? `do something` : `do something else`
let colour = width > 100 ? "red" : "blue";
Use case - change an element class list based on state:
var el = document.getElementById("loginButton");
loginInProgress ? el.classList.add("disabled") : el.classList.remove("disabled");
Bitwise Operators
Manipulate individual bits in a number - can prove to be incredibly efficient in certain applications.
-
AND (
&
) - returns a 1 in each bit position where the corresponding bits of both operands are 1; returns 0 if either bit in the compared position is 0.24 // binary: 0001 1000 10 // binary: 0000 1010 24 & 10 = 8 // binary: 0000 1000
-
OR (
|
) - returns a 1 in each bit position where the corresponding bits of either or both operands are 1; returns 0 if both bits are 0.24 // binary: 0001 1000 10 // binary: 0000 1010 24 | 10 = 26 // binary: 0001 1010
-
XOR (
^
) - returns a 1 in each bit position where the corresponding bits of either but not both operands are 1; returns 0 if both bits are 0 or both are 1.24 // binary: 0001 1000 10 // binary: 0000 1010 24 ^ 10 = 18 // binary: 0001 0010
- NOT (
~
) - unary operator that inverts the bits of its operand. This will also flip the sign bit (the leftmost) so the result is negative.NB: Bitwise operators treat their operands as a sequence of 32 bits.
10 // binary: 00000000 00000000 00000000 00001010 ~10 = -11 // binary: 11111111 11111111 11111111 11110101
-
Left Shift (
<<
) - shifts the bits of the first operand to the left by the number of positions specified by the second operand - effectively a multiplication with 2 to the power of the second operand.10 // binary: 0000 1010 10 << 2 = 40 // binary: 0100 1000
-
Sign Propagating Right Shift (
>>
) - shifts the bits of the first operand to the right by the number of positions specified by the second operand. The leftmost bits are filled with the sign bit (0 for positive numbers and 1 for negative numbers).10 // binary: 0000 1010 10 >> 2 = 2 // binary: 0000 0010 -10 // binary: 1111 1111 1111 1111 1111 1111 1111 0110 -10 >> 2 = -3 // binary: 1111 1111 1111 1111 1111 1111 1111 1101
-
Zero-fill Right Shift (
>>>
) - shifts the bits of the first operand to the right by the number of positions specified by the second operand. The leftmost bits are filled with 0s.10 // binary: 1010 10 >>> 2 = 2 // binary: 0010 -10 // 11111111 11111111 11111111 11110110 -10 >>> 2 = 1073741821 // 00111111 11111111 11111111 11111101
Practical Examples
Fast and Efficient Odd-Even Check
Instead of using the modulo operator (%
), the bitwise AND operator (&
) can be used to quickly determine if a number is even or odd.
function isEven(num) {
return !(num & 1);
}
function isOdd(num) {
return num & 1;
}
console.log(isEven(10)); // prints: true
console.log(isOdd(5)); // prints: true
How? The bitwise AND will flip every bit to 0 with only the last one staying the same:
13 // binary: 1101
1 // binary: 0001
13 & 1 = 1 // binary: 0001
12 // binary: 1100
1 // binary: 0001
12 & 1 = 0 // binary: 0000
Flags - Extracting Specific Bits
Specific bits in a value can be used to signify features availability
function bitAtPosition(num, position) {
return (num & (1 << position)) >> position;
}
const number = 0b1011010; // decimal: 90
console.log(bitAtPosition(number, 3)); // prints: 1
console.log(bitAtPosition(number, 5)); // prints: 0
How?
1 << position
created a number with only one bit set to 1, at the position given - this is offen referred to as the mask- The bitwise AND is applied on the given number and the mask created above resulting in only the bit we’re interesting in being making it through
- the bit is then moved to the right based on its position - the result will contain the required bit
Comma Operator
- evaluate multiple expressions within a single statement
- used to separate two or more expressions that are included where only one expression is expected, and evaluates each of its operands (from left to right)
- returns the value of the last operand.
expr1, expr2, ... , exprN
Usage
Declaring or initialising multiple variables in a single statement:
let a = 1, b = 2, c = 3;
Variable assignment within a for loop:
for (let i = 0, j = 10; i <= 10; i++, j--) {
console.log(`i: ${i}, j: ${j}`);
}
Multiple expressions within a single statement:
let a, b;
(a = 5, b = a * 2);
console.log(a); // prints: 5
console.log(b); // prints: 10
Potential Pitfalls
- readability - overusing can make code harder to read and maintain.
- operator precedence - has the lowest precedence among JavaScript operators.
- side effects - expressions used with the comma operator can have unintended side effects,particularly when using functions or methods with side effects.
Unary Operators
- operate on a single operand
- can either precede or follow the operand
Unary Plus (+)
Used to convert its operand into a number - if the operand is already a number, it has no effect.
let x = "42";
let y = +a;
console.log(typeof y); // Outputs: number
console.log(y); // Outputs: 42
Unary Negation (-)
Converts non-numeric values into numbers before negating them.
let x = "42";
let y = -x;
console.log(typeof y); // prints: number
console.log(y); // prints: -42
Logical NOT (!)
let x = "42";
console.log(!x); // prints: false
let y;
console.log(!y); // prints: true
Bitwise NOT (~)
10 // binary: 00000000 00000000 00000000 00001010
~10 = -11 // binary: 11111111 11111111 11111111 11110101
Increment & Decrement
Increase or decrease the value of a numeric operand by 1. They come in two forms: prefix (++x
) and postfix (x++
). The difference between the two forms lies in the order of evaluation when used in an expression.
let x = 42;
let y = x++;
console.log(x); // prints: 43
console.log(y); // prints: 42
y = ++x;
console.log(y); // prints: 44
console.log(y); // prints: 44
typeof
Returns a string representing the data type of its operand.
let x = 42;
let y = "Hello, World!";
console.log(typeof x); // prints: number
console.log(typeof y); // prints: string
void
Evaluates an expression and returns undefined. Practical usage: when a function should not return any value (e.g. hyperlink onclick event handlers).
function doSomething() {
console.log("Action performed");
return "result";
}
let result = void doSomething();
console.log(result); // undefined
delete
Removes a property from an object - returns true if the deletion is successful or if the property doesn’t exist.
const person = {
name: 'Charlie',
age: 30
};
console.log(delete person.age); // prints: true
Spread Operator
Expands an iterable varable, in place, where a list of values is expected.
let colours = ['red', 'blue', 'green'];
let pastels = ['pink', 'lavender', 'peach'];
colours.push(pastels);
console.log(colours);
// Outputs: Array(4) [ "red", "blue", "green", (3) […] ]
// vs.
colours.push(...pastels);
console.log(colours);
// Outputs: Array(6) [ "red", "blue", "green", "pink", "lavender", "peach" ]
The spread operator is equivalent to pushing individual elements:
colours.push(pastels[0], pastels[1], pastels[2]);
Merge arrays
let colours = ['red', 'blue', 'green'];
let pastels = ['pink', 'lavender', 'peach'];
let allColours = [ ...colours, ...pastels ];
console.log(colours);
// Outputs: Array(6) [ "red", "blue", "green", "pink", "lavender", "peach" ]
Object copy
let colour = { name: 'blue', score: 42};
let anotherColour = colour;
anotherColour.name = 'red';
console.log(colour.name); // Outputs: 'red'
To create a copy:
let colour = { name: 'blue', score: 42};
let anotherColour = { ...colour };
anotherColour.name = 'red';
console.log(colour.name); // Outputs: 'blue'
Combine objects
let colour = { name: 'blue', score: 42};
let apiColour = { id: 25, isActive: true};
let fullColour = { ...apiColour, ...colour };
console.log(fullColour);
// Outputs: Object { id: 25, isActive: true, name: "blue", score: 42 }
Destructuring
The process of unpacking data from arrays or objects, into distinct variables.
Destructuring from arrays
let colours = ['red', 'blue', 'green'];
let [colour1, colour2, colour3, colour4] = colours;
console.log(colour1); // 'red'
console.log(colour2); // 'blue'
console.log(colour4); // undefined
Default values can be provides:
let colours = ['red', 'blue', 'green'];
let [colour1, colour2, colour3, colour4 = 'yellow'] = colours;
console.log(colour4); // 'yellow'
Ignore certain values when required - notice the extra comma between colour1
and colour2
in the example below:
let colours = ['red', 'blue', 'green'];
let [colour1, , colour2] = colours;
console.log(colour2); // 'green'
Destructuring assignment
Assign values to multiple variable at the same time:
let colour, score;
[colour, score] = ['blue', 42];
Destructuring from objects
Similar to the destructuring from arrays but the names of the variables to destructure to must match the name of the object properties:
let colour = { name: 'blue', score: 42};
let {name, score} = colour;
console.log(name); // 'blue'
console.log(score); // 42
Unpack to different variable names:
let colour = { name: 'blue', score: 42};
let {name: colourName, score: colourScore} = colour;
console.log(colourName); // 'blue'
console.log(colourScore); // 42
Default values can be supplied
let colour = { name: 'blue', score: 42};
let {id: colourId = 1, name: colourName, score: colourScore} = colour;
console.log(colourId); // 1