JavaScript operators are symbols that perform operations on operands (values and variables). Understanding operators is fundamental to writing effective JavaScript code, as they allow you to manipulate data, make comparisons, and control program flow.
In this tutorial, we'll explore different types of operators including arithmetic, assignment, comparison, logical, bitwise, and more, along with their precedence and best practices.
Arithmetic Operators
Arithmetic operators perform mathematical calculations on numeric values.
Basic Arithmetic
// Addition (+)
let sum = 10 + 5; // 15
let textConcat = "Hello" + " " + "World"; // "Hello World"
// Subtraction (-)
let difference = 10 - 5; // 5
// Multiplication (*)
let product = 10 * 5; // 50
// Division (/)
let quotient = 10 / 5; // 2
let floatDivision = 10 / 3; // 3.333...
// Modulus/Remainder (%)
let remainder = 10 % 3; // 1
let isEven = number % 2 === 0; // Check if number is even
// Exponentiation (**) - ES6
let power = 2 ** 3; // 8
let squareRoot = 16 ** 0.5; // 4
// Increment (++)
let count = 5;
count++; // 6 (postfix)
++count; // 7 (prefix)
// Decrement (--)
count--; // 6 (postfix)
--count; // 5 (prefix)
Prefix vs Postfix
// Prefix increment/decrement
let x = 5;
let y = ++x; // x becomes 6, y is 6
console.log(x, y); // 6, 6
// Postfix increment/decrement
let a = 5;
let b = a++; // a becomes 6, b is 5
console.log(a, b); // 6, 5
// In expressions
let num = 10;
let result1 = ++num * 2; // 22 (11 * 2)
let result2 = num++ * 2; // 22 (11 * 2), then num becomes 12
Assignment Operators
Assignment operators assign values to variables and can perform operations in the process.
Basic Assignment
// Simple assignment (=)
let x = 10;
let y = "Hello";
// Multiple assignment
let a = b = c = 5; // All variables get value 5
// Destructuring assignment
const { name, age } = user;
const [first, second] = array;
// Assignment with arithmetic operators
let num = 10;
// Addition assignment (+=)
num += 5; // num = num + 5; // 15
// Subtraction assignment (-=)
num -= 3; // num = num - 3; // 12
// Multiplication assignment (*=)
num *= 2; // num = num * 2; // 24
// Division assignment (/=)
num /= 4; // num = num / 4; // 6
// Modulus assignment (%=)
num %= 4; // num = num % 4; // 2
// Exponentiation assignment (**=) - ES6
num **= 3; // num = num ** 3; // 8
Advanced Assignment
// String concatenation assignment
let text = "Hello";
text += " World"; // "Hello World"
// Array methods with assignment
let arr = [1, 2, 3];
arr[0] += 10; // arr[0] = arr[0] + 10
// Object property assignment
let obj = { count: 0 };
obj.count += 1; // obj.count = obj.count + 1
// Chained assignment
let a, b, c;
a = b = c = 100; // All become 100
// Assignment in conditions
let x;
if ((x = calculateValue())) {
console.log('Value is truthy:', x);
}
Comparison Operators
Comparison operators compare two values and return a boolean result.
Equality Operators
// Loose equality (==) - type coercion
5 == 5; // true
5 == "5"; // true (string converted to number)
0 == false; // true (false converted to 0)
null == undefined; // true
// Strict equality (===) - no type coercion
5 === 5; // true
5 === "5"; // false (different types)
0 === false; // false (different types)
null === undefined; // false (different types)
// Loose inequality (!=)
5 != "5"; // false
5 != 6; // true
// Strict inequality (!==)
5 !== "5"; // true
5 !== 6; // true
// Best practice: Always use strict equality
if (value === null) {
// More predictable behavior
}
// Object comparison
const obj1 = { a: 1 };
const obj2 = { a: 1 };
const obj3 = obj1;
obj1 === obj2; // false (different objects)
obj1 === obj3; // true (same reference)
Relational Operators
// Greater than (>)
10 > 5; // true
10 > 10; // false
// Less than (<)
5 < 10; // true
5 < 5; // false
// Greater than or equal (>=)
10 >= 10; // true
10 >= 5; // true
// Less than or equal (<=)
5 <= 5; // true
5 <= 10; // true
// String comparison (lexicographical)
"apple" < "banana"; // true
"z" > "a"; // true
"10" < "2"; // true (string comparison, not numeric)
10 < 2; // false (numeric comparison)
// Date comparison
const date1 = new Date('2023-01-01');
const date2 = new Date('2023-01-02');
date1 < date2; // true
Logical Operators
Logical operators perform boolean operations and are often used for conditional logic.
Basic Logical Operators
// Logical AND (&&)
true && true; // true
true && false; // false
false && true; // false
false && false; // false
// Short-circuit evaluation
let x = 5;
(x > 0) && console.log('Positive'); // Logs 'Positive'
(x < 0) && console.log('Negative'); // Doesn't log
// Using && for conditional execution
function greet(name) {
name && console.log(`Hello, ${name}!`);
}
greet('John'); // Logs "Hello, John!"
greet(''); // Doesn't log
// Default values
const options = options || {}; // Common pattern
// Logical OR (||)
true || true; // true
true || false; // true
false || true; // true
false || false; // false
// Short-circuit for default values
let name = userName || "Guest";
let port = config.port || 3000;
// Logical NOT (!)
!true; // false
!false; // true
!0; // true
!1; // false
!""; // true
!"hello"; // false
// Double NOT for boolean conversion
!!"hello"; // true
!!0; // false
!!null; // false
Advanced Logical Operators
// Nullish coalescing operator (??) - ES2020
const value = null ?? 'default'; // 'default'
const value2 = undefined ?? 'default'; // 'default'
const value3 = 0 ?? 'default'; // 0 (0 is not null/undefined)
const value4 = '' ?? 'default'; // '' (empty string is not null/undefined)
// Comparison with ||
const orValue = 0 || 'default'; // 'default' (0 is falsy)
const nullishValue = 0 ?? 'default'; // 0 (0 is not null/undefined)
// Practical usage
const settings = {
timeout: 0,
retries: null
};
const timeout = settings.timeout ?? 5000; // 0
const retries = settings.retries ?? 3; // 3
// Logical assignment operators - ES2021
let x;
x ||= 5; // Equivalent to: x = x || 5
let y = 0;
y ||= 5; // y remains 0 (0 is falsy)
let z;
z ??= 10; // Equivalent to: z = z ?? 10
// Combining operators
const result = (a > b) && (c < d) || (e === f);
// Same as: ((a > b) && (c < d)) || (e === f)
Bitwise Operators
Bitwise operators perform operations on binary representations of numbers.
Basic Bitwise Operations
// Bitwise AND (&)
5 & 3; // 1 (0101 & 0011 = 0001)
// Bitwise OR (|)
5 | 3; // 7 (0101 | 0011 = 0111)
// Bitwise XOR (^)
5 ^ 3; // 6 (0101 ^ 0011 = 0110)
// Bitwise NOT (~)
~5; // -6 (complement of 0101 is 1010)
// Left shift (<<)
5 << 1; // 10 (0101 << 1 = 1010)
5 << 2; // 20 (0101 << 2 = 10100)
// Right shift (>>)
20 >> 2; // 5 (10100 >> 2 = 00101)
// Zero-fill right shift (>>>)
-5 >>> 1; // 2147483645 (fills with zeros)
// Practical uses
// Check if number is even
function isEven(num) {
return (num & 1) === 0;
}
// Swap variables without temporary variable
let a = 5, b = 3;
a = a ^ b;
b = a ^ b;
a = a ^ b;
// a = 3, b = 5
// Round down to nearest power of 2
function roundToPowerOf2(num) {
return num & (num - 1);
}
String Operators
JavaScript provides special operators for string manipulation and concatenation.
String Concatenation
// Plus operator for concatenation
let greeting = "Hello" + " " + "World"; // "Hello World"
let result = "The answer is " + 42; // "The answer is 42"
// Concatenation assignment
let text = "Hello";
text += " World"; // "Hello World"
// Template literals (ES6) - preferred method
let name = "John";
let age = 30;
let message = `My name is ${name} and I am ${age} years old`;
// Multi-line strings
let multiline = `This is a
multi-line
string`;
// Expression evaluation in template literals
let calculation = `The result is ${2 + 2 * 3}`; // "The result is 8"
// Tagged template literals
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] ? `${values[i]}` : '';
return result + str + value;
}, '');
}
let highlighted = highlight`Name: ${name}, Age: ${age}`;
// "Name: John, Age: 30"
Type Operators
Type operators help determine the type of values and check for specific types.
typeof Operator
// typeof operator
typeof 42; // "number"
typeof "hello"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" (historical bug)
typeof {}; // "object"
typeof []; // "object"
typeof function() {}; // "function"
typeof Symbol(); // "symbol"
// Practical usage
function processValue(value) {
if (typeof value === 'string') {
return value.toUpperCase();
} else if (typeof value === 'number') {
return value.toFixed(2);
}
return value;
}
// Type checking patterns
const isString = value => typeof value === 'string';
const isNumber = value => typeof value === 'number' && !isNaN(value);
const isBoolean = value => typeof value === 'boolean';
const isFunction = value => typeof value === 'function';
instanceof Operator
// instanceof operator
const arr = [1, 2, 3];
arr instanceof Array; // true
arr instanceof Object; // true
const date = new Date();
date instanceof Date; // true
date instanceof Object; // true
class Person {}
const john = new Person();
john instanceof Person; // true
john instanceof Object; // true
// Practical usage
function processCollection(collection) {
if (collection instanceof Array) {
return collection.map(item => item * 2);
} else if (collection instanceof Set) {
return Array.from(collection).map(item => item * 2);
}
return [];
}
// Custom instanceof checks
class Animal {}
class Dog extends Animal {}
const dog = new Dog();
dog instanceof Dog; // true
dog instanceof Animal; // true
dog instanceof Object; // true
in Operator
// in operator for object properties
const person = {
name: 'John',
age: 30
};
'name' in person; // true
'age' in person; // true
'email' in person; // false
// Check for inherited properties
'toString' in person; // true (inherited from Object)
// in operator for arrays
const fruits = ['apple', 'banana', 'orange'];
0 in fruits; // true (index 0 exists)
1 in fruits; // true
3 in fruits; // false (index 3 doesn't exist)
'length' in fruits; // true (length property exists)
// Practical usage
function hasProperty(obj, prop) {
return prop in obj;
}
function safeAccess(obj, prop) {
return prop in obj ? obj[prop] : undefined;
}
Operator Precedence
Operator precedence determines the order in which operations are performed.
Precedence Rules
// High to low precedence
// 1. Grouping ()
// 2. Member access . []
// 3. Function call ()
// 4. Unary operators ++ -- ! ~ + - typeof void delete
// 5. Exponentiation ** (right-to-left)
// 6. Multiplication * / %
// 7. Addition + -
// 8. Bitwise shift << >> >>>
// 9. Relational < <= > >= in instanceof
// 10. Equality == === != !==
// 11. Bitwise AND &
// 12. Bitwise XOR ^
// 13. Bitwise OR |
// 14. Logical AND &&
// 15. Logical OR ||
// 16. Nullish coalescing ??
// 17. Conditional ?:
// 18. Assignment = += -= *= /= %= **= <<= >>= >>>= &= ^= |= &&= ||= ??=
// 19. Comma ,
// Examples
let result = 2 + 3 * 4; // 14 (multiplication first)
result = (2 + 3) * 4; // 20 (parentheses override)
let value = 5 > 3 && 2 < 4; // true
// Same as: (5 > 3) && (2 < 4)
let x = 10, y = 5;
let result2 = x > y ? x : y; // 10
// Complex expressions
let score = 80;
let grade = score >= 90 ? 'A' :
score >= 80 ? 'B' :
score >= 70 ? 'C' : 'F'; // 'B'
Associativity
// Left-to-right associativity
let result = 10 - 5 - 2; // 3
// Same as: (10 - 5) - 2
let text = "a" + "b" + "c"; // "abc"
// Same as: ("a" + "b") + "c"
// Right-to-left associativity
let power = 2 ** 3 ** 2; // 512
// Same as: 2 ** (3 ** 2) = 2 ** 9 = 512
let x = y = z = 5; // All become 5
// Same as: x = (y = (z = 5))
// Assignment operators are right-to-left
x += y += 5; // y = y + 5, then x = x + y
Conditional (Ternary) Operator
The conditional operator provides a concise way to write conditional expressions.
Basic Usage
// Syntax: condition ? value_if_true : value_if_false
let age = 18;
let message = age >= 18 ? "Adult" : "Minor";
console.log(message); // "Adult"
// In expressions
let price = 100;
let discount = price > 50 ? 0.1 : 0.05;
let finalPrice = price * (1 - discount);
// Nested ternary operators
let score = 85;
let grade = score >= 90 ? 'A' :
score >= 80 ? 'B' :
score >= 70 ? 'C' : 'F';
console.log(grade); // 'B'
// Function return
function getFee(isMember) {
return isMember ? '$2.00' : '$10.00';
}
// Default values
let username = input.value ? input.value : 'Guest';
Advanced Patterns
// Multiple conditions
let result = condition1 ? value1 :
condition2 ? value2 :
condition3 ? value3 : defaultValue;
// With function calls
let output = isValid(data) ? processData(data) : showError();
// In template literals
let status = isActive ? 'Active' : 'Inactive';
let badge = `${status}`;
// Object property assignment
let config = {
theme: isDark ? 'dark' : 'light',
animations: prefersAnimations ? true : false
};
// Array methods with ternary
let numbers = [1, 2, 3, 4, 5];
let processed = numbers.map(n => n % 2 === 0 ? n * 2 : n + 1);
// [2, 3, 4, 8, 6]
Comma Operator
The comma operator evaluates multiple expressions and returns the value of the last one.
Comma Operator Usage
// Basic comma operator
let a = (1, 2, 3); // a = 3 (last expression)
let b = (x = 5, y = 10, x + y); // b = 15
// In for loops
for (let i = 0, j = 10; i < 5; i++, j--) {
console.log(i, j); // 0,10 1,9 2,8 3,7 4,6
}
// Multiple variable declarations
let x = 1, y = 2, z = 3;
// In function calls
console.log((a = 5, b = 10, a + b)); // 15, a=5, b=10
// Practical usage (rarely needed)
let i, j;
for (i = 0, j = array.length - 1; i < j; i++, j--) {
// Swap array elements
[array[i], array[j]] = [array[j], array[i]];
}
Optional Chaining Operator
The optional chaining operator (?.) provides a safe way to access nested object properties.
Optional Chaining Usage
// Optional chaining (?.) - ES2020
const user = {
name: 'John',
address: {
street: '123 Main St',
city: 'New York'
}
};
// Without optional chaining
const city = user && user.address && user.address.city; // 'New York'
// With optional chaining
const city2 = user?.address?.city; // 'New York'
// Safe property access
const zipCode = user?.address?.zipCode; // undefined (no error)
// Optional method calls
const result = user?.getProfile?.(); // Only calls if method exists
// Optional array access
const firstItem = array?.[0]; // undefined if array is null/undefined
// Chaining with function calls
const data = response?.data?.items?.[0]?.name; // Safe nested access
// Practical usage
function getUserDisplayName(user) {
return user?.profile?.displayName || user?.name || 'Anonymous';
}
// With optional chaining and nullish coalescing
const timeout = config?.timeout ?? 5000;
const retries = config?.retries ?? 3;
Best Practices
Guidelines for using operators effectively and avoiding common pitfalls.
Type Safety
// Use strict equality (===) instead of loose (==)
if (value === null) {
// More predictable
}
// Use typeof for type checking
if (typeof value === 'string') {
// Clear intent
}
// Use optional chaining for safe property access
const name = user?.profile?.name ?? 'Unknown';
// Use nullish coalescing for default values
const timeout = config?.timeout ?? 3000; // Better than ||
Readability
// Use parentheses for complex expressions
const result = (a + b) * (c - d) / e;
// Break down complex conditions
const isEligible = age >= 18 &&
hasValidId &&
hasNoViolations;
if (isEligible) {
// Process application
}
// Use descriptive variable names
const hasPermission = user.role === 'admin' || user.role === 'moderator';
const isActive = user.status === 'active' && user.lastLogin > thirtyDaysAgo;
// Avoid overly complex ternary chains
// Bad:
const result = a > b ? a > c ? a : c : b > c ? b : c;
// Better:
const max = Math.max(a, b, c);
Performance
// Use short-circuiting for efficiency
const expensiveResult = condition && expensiveFunction();
// Cache property access
const length = array.length;
for (let i = 0; i < length; i++) {
// Process array[i]
}
// Use appropriate operators
// For checking existence:
'property' in obj; // Better than obj.hasOwnProperty('property')
// For array length:
arr.length; // Direct property access
// Use bitwise operators when appropriate
const isEven = (num & 1) === 0; // Faster than num % 2 === 0
Common Pitfalls
Avoid these common mistakes when working with JavaScript operators.
Type Coercion Issues
// Loose equality pitfalls
0 == false; // true
"" == false; // true
"0" == false; // true
null == undefined; // true
// Always use strict equality
0 === false; // false
"" === false; // false
"0" === false; // false
null === undefined; // false
// String vs number comparison
"10" < 2; // true (string comparison)
10 < 2; // false (numeric comparison)
// Explicit conversion
Number("10") < 2; // false
Operator Precedence
// Unexpected behavior due to precedence
let result = 5 + 3 * 2; // 11, not 16
// Use parentheses: (5 + 3) * 2 = 16
// Type conversion in expressions
let sum = "5" + 3; // "53" (concatenation)
let sum2 = Number("5") + 3; // 8 (addition)
// Increment in expressions
let x = 5;
let y = x++ + 5; // y = 10, x = 6
let z = ++x + 5; // z = 12, x = 7
Falsy Values
// Falsy values can cause unexpected behavior
const values = [0, "", false, null, undefined, NaN];
// Using || for default values
let value = 0 || "default"; // "default" (0 is falsy)
let better = 0 ?? "default"; // 0 (nullish coalescing)
// Checking for empty strings
let text = "";
if (text) {
// This won't execute
}
// Better check
if (text.length > 0) {
// This works as expected
}
Summary
Key Takeaways
- JavaScript provides diverse operators for different operations
- Operator precedence determines evaluation order
- Type coercion can cause unexpected behavior
- Modern operators (??, ?. , ??=) improve code safety
- Strict equality is preferred over loose equality
Best Practices
- Use strict equality (===) for comparisons
- Use optional chaining for safe property access
- Prefer nullish coalescing over logical OR for defaults
- Use parentheses for complex expressions
- Choose appropriate operators for the task
Common Pitfalls
- Forgetting operator precedence rules
- Using loose equality instead of strict
- Not understanding type coercion
- Confusing falsy values with null/undefined
- Overusing complex ternary expressions