Expressions

Expressions are something that can be evaluated to produce a value.

Primary expressions

These are the simplest kind of expressions: either a literal value, or a single variable reference.

3.14      // A number literal
false     // A boolean literal
'Hello'   // A string literal
null      // A null value
undefined // An undefined value
/regex/   // A RegExp literal
this      // reference to a single variable

Property access expressions

If you have a reference to an object type, you can access its properties using the . or [] operator. With the [] operator, you can access the property name using an expression.

const user = { firstName: 'John', lastName: 'Doe' };
const foods = ['noodle', 'sushi'];

// Using the . operator
console.log(user.firstName); //=> John

// Using the [] operator
console.log(user['last' + 'Name']); //=> Doe
console.log(foods[0]); //=> noodle

Accessing property of null or undefined

If a is either null or undefined, you will get a TypeError when you try to do a.b or a['b'].

To prevent this, you could first check that the value of a is not falsy (which covers the case if it is null or undefined), or use optional chaining (see below).

// This will not throw if `user` is falsy
const firstName = user && user.firstName;

// This strategy could be cumbersome
// if you want to access `obj.a.b.c`
const value = obj && obj.a && obj.a.b && obj.a.b.c;

Optional chaining

You can use ?. or ?[] to only access a property if the lefthand side is not null or undefined (ES2020).

If the lefthand side is null or undefined, the whole expression will be evaluated as undefined.

const obj = {
  a: { b: 123 }s
};

// Won't throw error
console.log(obj?.a?.b); //=> 123
console.log(obj?.a?.b?.c?.d); //=> undefined

You can also use ?.() to optionally call a function at the end of a property access expression.

Note that it only checks that the lefthand side is not null or undefined. It does NOT check if the lefthand side is actually a function.

Call expression

A call expression invokes a function. The result of this expression is the return value of the called function, or undefined if it does not return a value.

Just put the name of the function (or an expression that evaluates to a function), and append it with (), optionally passing the arguments between the ( and ).

function sortArray(arr) {
  return arr.sort();
}

const arr = [1, 5, 2, 4, 3];
console.log(sortArray(arr));

Operators

Operators perform arithmetic expressions, comparison expressions, logical expressions, assignment expressions, and more.

There is a list of operator precendence, but for simplicity, remember PEMDAS in math. Use () to dictate which operation should go first.

Arithmetic operators

Increment/decrement operators

  • a++ return the current value of a, then increment the value of a by 1
  • a-- return the current value of a, then decrement the value of a by 1
  • ++a increment the value of a by 1, then return the new value of a
  • --a decrement the value of a by 1, then return the new value of a

Common math operators

  • a + b (addition) adds a and b
  • a - b (subtraction) subtracts a with b
  • a * b (multiplication) multiply a and b
  • a / b (division) divide a with b
  • a % b (modulo) computes the reminder of a divided by b
  • a ** b (exponentation) — compute a to the power of b
  • -a negate the value of a (plus to minus, vice versa)

Bitwise operators

  • a & b (bitwise AND) performs boolean AND operation on each bit of a and b, e.g. 0b0011 & 0b0101 0b0001
  • a | b (bitwise OR) performs boolean OR operation on each bit of a and b, e.g. 0b0011 | 0b0101 0b0111
  • a ^ b (bitwise XOR) performs boolean XOR operation on each bit of a and b, e.g. 0b0011 ^ 0b0101 0b0110
  • ~a (bitwise NOT) inverts all bits of a

Shift operators

  • a << b (shift left) shifts in b zero bits to the right of a (the left-most b bits are discarded), e.g. 0b10101100_00000000_00000000_00001111 << 2 0b10110000_00000000_00000000_00111100
  • a >> b (shift right with zero) shifts in b zero bits to the left of a (the right-most b bits are discarded), e.g. 0b10110000_00000000_00000000_11110101 >> 2 0b00101100_00000000_00000000_00111101
  • a >>> b (shift right with sign) shifts in b sign bits (the first bit of a) to the left of a (the right-most b bits are discarded), e.g. 0b10110000_00000000_00000000_11110101 >> 4 0b11111011_00000000_00000000_00001111

Logical operators

These may sound like they perform boolean operators, but they don't.

These operators can operate on non-boolean values. They just consider the truthiness/falsiness of their operands, and return the corresponding value.

Falsy values

These are the falsy values in JavaScript: false, undefined, null, 0, NaN, '' (the empty string).

Anything that is not falsy is truthy.

  • a && b (logical AND) if a is falsy, return a (the falsy value), otherwise return b
  • a || b (logical OR) if a is truthy, return a (the truthy value), otherwise return b
  • !a (logical NOT) if a is truthy, return false, otherwise return true

Relational operators

Comparison operators

Comparison operators work on numbers and strings (any other types are converted first). For string comparison, remember that JavaScript strings are just sequences of 16-bit values (it compares which string has the first smaller/larger value from left to right).

If any of the operand is NaN, these comparisons always return false.

  • a < b if a < b, return true, otherwise return false
  • a > b if a > b, return true, otherwise return false
  • a <= b if a b, return true, otherwise return false
  • a >= b if a b, return true, otherwise return false

Equality operators

There are loose (==) and strict (===) equality operators in JavaScript. The Loose equality operator performs type coercion before comparing the values, while strict equality operator never performs type coercion (if they are not the same type, then the result will be false).

Whenever possible, consider using === and !== over == and !=.

  • a == b (loose equality) return true if a is "equal" to b (after type conversion), otherwise return false; e.g. '1' == 1 true
  • a === b (strict equality) return true if a is equal to b and they are of the same type, otherwise return false; e.g. '1' === 1 false, but 1 === 1 true
  • a != b (loose inequality) inverse of a == b
  • a !== b (strict inequality) inverse of a === b

Caveats/surprises (blame JavaScript magic):

  • NaN === NaN false
  • null == false false

Checking for nullish value

You can use a == null to check if a is nullish (null or undefined).

Assignment operators

The = operator

This simply assigns the right-hand side expression to a variable, an object property, or an array element. The return value of an assignment expression is the right-hand side value.

let a = 0;
a = (2 * 4) + 3;
console.log(a); //=> 11
console.log((a = 3) < 5); //=> true
console.log(a); //=> 3

const obj = { x: 1 };
obj.x = 2;
console.log(obj.x); //=> 2

const arr = [1, 2, 3];
arr[0] = 5;
console.log(arr); //=> [ 5, 2, 3 ]

Assignment with operation

These are essentially shorthands for an assignment operation.

  • a += b equivalent to a = a + b
  • a -= b equivalent to a = a - b
  • a *= b equivalent to a = a * b
  • a /= b equivalent to a = a / b
  • a %= b equivalent to a = a % b
  • a **= b — equivalent toa = a ** b
  • a <<= b equivalent to a = a << b
  • a >>= b equivalent to a = a >> b
  • a >>>= b equivalent to a = a >>> b
  • a &= b equivalent to a = a & b
  • a |= b equivalent to a = a | b
  • a ^= b equivalent to a = a ^ b

Miscellaneous

Ternary operator

  • a ? b : c if a is truthy, then return b, otherwise return c

Concatenation operator

  • a + b if a or b is string, concatenates a and b, e.g. 'Hello' + 'World' HelloWorld

The ?? operator

  • a ?? b (nullish coalescing operator) if a is not nullish (not null and not undefined), then return a, otherwise return b

Checking if a variable is defined

Use typeof myVar !== 'undefined' to check if a variable myVar has been defined.

If you use myVar !== undefined instead (note the subtleties), JavaScript will throw ReferenceError if myVar has not been defined.

The in operator

Checks if a property (a string, a number, or a Symbol) exists in an object.

const obj = { a: 1, b: 2, c: 3 };
console.log('a' in obj); //=> true
console.log('z' in obj); //=> false

const arr = [0, 1, 2];
console.log(0 in arr); //=> true
console.log(4 in obj); //=> false

The delete operator

Deletes an object property or an array element. It returns true if the property/element existed, or false otherwise.

Note that it is not the same as assigning an undefined value to the object property or the array element. The original "key" is removed entirely (using the in operator will return false).

const obj = { a: 1, b: 2, c: 3 };

// These are not the same operation
obj.b = undefined;
delete obj.c;

console.log(obj); //=> { a: 1, b: undefined }
console.log(obj.b, 'b' in obj); //=> undefined true
console.log(obj.c, 'c' in obj); //=> undefined false

The typeof operator

Returns the "type" of its operand, which is one of: number, boolean, string, symbol, object, function, or bigint. Note that typeof null 'object' ("null is a special kind of object").

console.log(typeof 2); //=> number
console.log(typeof 'aloha'); //=> string
console.log(typeof someUndefinedVariable); //=> undefined
console.log(typeof null); //=> object

The instanceof operator

Checks if an object is an instance of a class. That is, obj instanceof Class returns true if a inherits from Class.prototype, directly or indirectly. (It will become clear once we talk about classes.)

let date = new Date();
// Remember that Date inherits from Object
console.log(date instanceof Date); //=> true
console.log(date instanceof Object); //=> true
console.log(date instanceof Number); //=> false

The void operator

Discards the result of an expression, returning undefined.

console.log(void (1 + 3)); //=> undefined

The , operator

a, b evaluates a, then evaluates b, then returns b.

// Look at the `i++, j--` part
for(let i=0, j=5; i < j; i++, j--) {
  console.log(i, j);
}

References