Operators

Expressions and operators

Operators are part of an expression. An expression is a piece of code that evaluates to a value. Expressions are usually part of statements.

For instance; let a = 2; is a statement in which a = 2 is an expression and in which = is an operator.

The expression a = 2 is an assignment, but the expression itself also evaluates to a value: assignments have a return value (later more about this):


let a;
console.log(a = 2); // logs: 2 // logs the assignments' return value.

An operator is a sign or keyword mostly relating two operands. JavaScript has multiple types of operators:


let counter = 0; // operator: =
while (counter < 10) { // operator: <
  if (counter % 2 === 0) { // operators: % and ===
    console.log(counter + " is even"); // operator: +
  } else {
    console.log(counter + " is odd"); // operator: +
  }
  counter += 1; // operator: +=
}

Assignment operators

A value can be assigned to a variable or constant by using an assignment operator. The initial assignment must always use the "equal to" sign (=) as operator, e.g. let a = b;. In this context the "equal to" sign means "a gets assigned the value b" as opposed to its meaning in a mathematical context: "a equals b". Comparison operators do provide "equal to" operators as explained below.

All other assignment operators are compound assignment operators (like x += y). They are shorthand operators for assigning a variable its previous value which has an arithmetic operation performed on (see arithmetic operators below). The (compound) assignment operations are listed in the following table:

Table 1: Assignment operators
Name Shorthand operator Explanation
Assignment x = y
Addition assignment x += y Same as: x = x + y
Subtraction assignment x -= y Same as: x = x - y
Multiplication assignment x *= y Same as: x = x * y
Division assignment x /= y Same as: x = x / y
Remainder assignment x %= y Same as: x = x % y
Exponentiation assignment x **= y Same as: x = x ** y
Increment assignment x++ and ++x As a prefix operator (++x) it returns the value of its operand after adding 1.
As a postfix operator (x++) it returns the value of its operand before adding 1.
If x is 3 then:
++x sets x to 4 and returns 4 (same as x += 1).
x++ returns 3 and, only then, sets x to 4.
Decrement assignment x-- and --x As a prefix operator (--x) it returns the value of its operand after subtracting 1.
As a postfix operator (x--) it returns the value of its operand before subtracting 1.
If x is 3, then:
--x sets x to 2 and returns 2 (same as x -= 1).
x-- returns 3 and, only then, sets x to 2.

Like most expressions in JS, assignments have a return value. The return value matches the expression to the right of the =-sign.


let varA = 0,
    varB = 10;
	
console.log(varA = varB); // logs: 10 // it logs the return value varB (and varA is assigned the value 10)
console.log(varA += varB); // logs: 20 // varA = varA + varB thus it returns: varA + varB
console.log(varA); // logs: 20	
console.log(varB **= 3); // logs: 1000 // varB = varB ** 3 (varB = 10^3).

The increment and decrement assignments return as mentioned in table 1.


let varA = 0;

console.log(++varA); // logs: 1
console.log(varA); // logs: 1

varA = 0;

console.log(varA++); // logs: 0
console.log(varA); // logs: 1

let varB = 10;

console.log(--varB); // logs: 9
console.log(varB); // logs: 9

varB = 10;

console.log(varB--); // logs: 10
console.log(varB); // logs: 9

Compound assignment operators are often used in conjunction with loops. In the next code fragment i += 0.1 is the same as i = i + 0.1, which means that the "new" i gets assigned the "old" i plus 0.1. Thus i increases an increment of 0.1 every iteration in the while-loop.


let i = 0;
while (i < 10) {
  console.log(i);
  i += 0.1;
}

BTW: The seemingly inconsistent precision of the increments of 0.1 in the result is due to the properties of floating point numbers.

Comparison operators

A comparison operator compares both operands and returns a logical value (true or false) based on whether the comparison is true or not. For example 3 > 2 returns true.

Comparison operators are often used in conditional statements and loops.

Table 2: Comparison operators
Operator Description
a == b Equal: returns true if the operands are equal.
a != b Not equal: returns true if the operands are not equal.
a === b Strict equal: returns true if the operands are equal and of the same data type.
a !== b Strict not equal: returns true if the operands are of the same data type but not equal, or are of different data type.
a > b Greater than: returns true if a is greater than b.
a >= b Greater than or equal: returns true if a is greater than or equal to b.
a < b Less than: returns true if a is less than b.
a <= b Less than or equal: returns true if a is less than or equal to b.

Values are of some data type, e.g. a number, a string (text), a boolean (true or false), an object. Data types will be covered in detail later in this tutorial. Strict comparison operations (=== and !==) always return false when the two compared operands are of different data types. With all the other comparison operators JavaScript will automatically convert operands of different types to an appropriate type for the comparison. This is called type coercion. How JS executes this will be explained in more detail in the chapters type coercion and falsy & truthy. Greater than (or equal) and less than (or equal) operators generally coerce operands of different type to numbers and compare them numerically. If both operands are strings, then they are compared alphabetically.


let name = "Beth"; // A value enclosed in single or double quotation marks indicates a string value.
console.log('beth' == name); // logs: false // case sensitive!
	
console.log(2 == 2); // logs: true
console.log(2 != 5); // logs: true

console.log("313" == 313); // logs: true // The string "313" is coerced to number 313.
console.log("313" === 313); // logs: false // Non-matching data types.
console.log(false != 0); // logs: false // Boolean false is coerced to 0, which IS equal to 0.
console.log(false !== 0); // logs: true // Non-matching data types: they are indeed not strict equal. 

console.log(10 > 2); // logs: true
console.log(7 >= 7); // logs: true
console.log('Alice' < name); // logs: true // compared alphabetically (case sensitive!).
console.log('2' < '123'); // logs: false // compared alphabetically 2 is greater than 1.

console.log(true < 2); // logs: true // "true" automatically converted to 1, which is less than 2.

BTW: A string literal is signified by placing the string of whatever characters within quotation marks. Literals 'a' and "a" both signify the same string.

Arithmetic operators

Arithmetic operators are used in conjunction with numerical operands where the operation returns a single numerical value.

Table 3: Arithmetic operators
Operator Description Example
a + b Addition: returns the sum of the two operands. 2 + 3 returns 5.
a - b Subtraction: returns the result of a minus b. 8 - 3 returns 5.
a * b Multiplication: returns the product of the two operands. 3 * 5 returns 15.
a / b Division: returns the result of a divided by b. 6 / 2 returns 3.
a ** b Exponentiation: returns the result of a raised to the power of b. 2 ** 3 returns 8.
10 ** -1 returns 0.1.
a % b Remainder (modulo operation): returns the integer remainder of a divided by b. 12 % 5 returns 2.
-a Negation. Returns the negation of its operand. If x is 3, then -x returns -3.
If x is -3, then -x returns 3.
+a Unary plus. Attempts to convert the operand to a number, if it is not already. See explanation below

JavaScript also has a built-in object with properties and methods for mathematical constants and functions. This object is Math. For instance, Math.pow(3,2) returns 9 (3 to the exponent power 2).


let myVar;

console.log(Math.pow(3,2) === 3 ** 2); // logs: true
console.log(myVar = 2 - 5); // logs: -3
console.log(-myVar); // logs: 3
console.log(--3); // SyntaxError: invalid increment/decrement operand. // Assigning a value to a value is not possible
console.log(-(-3)); // logs: 3
console.log(7--3); // SyntaxError: invalid increment/decrement operand.
console.log(7-(-3) === 10); // logs: true	
console.log(2/0 === Infinity); // logs: true	
console.log(9**(1/2)); // logs: 3 // √9 = 3.
console.log(9**(1/2) === Math.sqrt(9)); // logs: true

When operands are not numbers, JS "type coerces" them to a number. More about this in chapter type coercion. An exception is the addition operator (a + b) performed on string operands. In that case the separate strings are concatenated to a single string. This also holds for the addition assignment operator, since a += b equals a = a + b.


console.log("Hello" + " you!"); // logs: "Hello you!"
let str = "Hello";
console.log(str += " you!"); // logs: "Hello you!"
console.log(str += " How ya doin'?"); // logs: "Hello you! How ya doin'?"

console.log(2 + 3); // logs: 5
console.log("2" + 3); // logs: "23" // string concatenation

BTW: The addition operator concatenates strings if at least one of both operands is a string.

With the unary plus operator (+a) you can explicitly convert operands of any data type to numbers.


console.log(+-3); // logs: -3.
console.log(+3); // logs: 3.
console.log(++3); // logs: SyntaxError: invalid increment/decrement operand.

console.log(+"3"); // logs: 3.
console.log(+true); // logs: 1.
console.log(+false); // logs: 0.

console.log(+"2" + 3); // logs: 5 

Logical operators

Logical operators (aka logical connectives) typically take operands with Boolean (i.e. logical) values true or false and return a Boolean value.

Table 4: Logical operators
Operator Description
a && b Logical AND (logical conjunction). Returns true if both operands are true; otherwise, it returns false.
a || b Logical OR (logical disjunction). Returns true if either operand is true; otherwise, if both are false, it returns false.
a ?? b Nullish coalescing operator. Returns b if a is null or undefined; otherwise it returns a. Technically not a logical operator. Will be explained below.
!a Logical NOT (logical negation). Returns false if its single operand is true; otherwise, if operand false, it returns true.

let x = 3,
    y = 11,
    z = true;

console.log( z && (y > 10) ); // logs: true // ⇐ true && true
console.log( (x !== 4) && (!(y === 11)) ); // logs: false // ⇐ true && false	
console.log( (x >= 3) || (y === 5) ); // logs: true // ⇐ true || false
console.log( (x === 5) || (y === 5) ); // logs: false // ⇐ false || false
console.log( !(x !== y) ); // logs: false // ⇐ not true

Operand values do not have to be precisely false or true. They may also be falsy or truthy. Falsy values are for instance 0, "" (empty string), null and undefined. All values that are not falsy are truthy. Falsy values are automatically converted to false and thruthy values are automatically converted to true. More about this in chapter falsy & truthy.

Short-circuiting

Above is stated that logical operations return a Boolean value true or false. This is actually not accurate. The next two functions do the same as the && and || operators. Note that only expr1 is evaluated (expr2 does not take effect in the evaluation), which is called short-circuiting. So, logical operations may also return a falsy or truthy value.


// &&: Return expr1 if expr1 can be converted to false; otherwise, return expr2.
function logicalAND(expr1, expr2) {
  if (!expr1) { return expr1; }
  else { return expr2; }
}

// ||: Return expr1 if expr1 can be converted to true; otherwise, return expr2. 
function logicalOR(expr1, expr2) {
  if (expr1) { return expr1; }
  else { return expr2; }
}

console.log( logicalAND("2" == 2, '7' >= 7) ); // logs: true // Same as: ("2" == 2) && ('7' >= 7).
console.log( logicalOR(2 === 3, 'John') ); // logs: "John" // Same as: 2 === 3 || 'John'.
console.log( logicalOR('', 'Jane') ); // logs: "Jane" // Same as: "" || 'Jane'. "" is a falsy value.	
console.log( logicalOR('John', 'Jane') ); // logs: "John" // Same as: 'John' || 'Jane'. 'John' is a truthy value.
console.log( logicalAND(0, 2 === 2) ); // logs: 0 // Same as: 0 && 2 === 2. 0 is a falsy value. 
console.log( logicalOR(1, false) ); // logs: 1 // Same as: 1 || false. 1 is a truthy value.

Nullish coalescing operator

As an alternative to the logical OR (||) you can use the nullish coalescing operator (??). It only returns the second operand if the first one is nullish, i.e. either null or undefined. The logical OR (||) returns the right-hand side operand if the left operand is any falsy value, not only if null or undefined.

Next function does the same as the ?? operator. Also the nullish coalescing operator evaluates short-circuit; the right-hand side operand does not take effect in the evaluation.


// ??: Return expr1 if expr1 is NOT null or undefined; otherwise, return expr2.
function nullishCoalescing(expr1, expr2) {
  if (expr1 != null) { return expr1; }
  else { return expr2; }
}

console.log( nullishCoalescing(null, 'Hello') ); // logs: "Hello" // Same as: null ?? 'Hello'.
console.log( nullishCoalescing(undefined, 'Hello') ); // logs: "Hello" // Same as: undefined ?? 'Hello'.
console.log( nullishCoalescing(false, 'Hello') ); // logs: false // Same as: false ?? 'Hello'.
console.log( nullishCoalescing("", 'Hello') ); // logs: "" // Same as: "" ?? 'Hello'.

This short-circuiting is often used to assign a default value to a variable or constant.


let updateText, updateNumber, currentText, currentNumber;
const defaultText = "John/Jane Doe",
      defaultNumber = 1;

updateText = null;
currentText = updateText || defaultText;
console.log(currentText); // logs: "John/Jane Doe"

updateNumber = 2;
currentNumber = updateNumber || defaultNumber;
console.log(currentNumber); // logs: 2

// 0 may be a valid value in this case, but 0 is falsy and thus defaultNumber is assigned  
updateNumber = 0;
currentNumber = updateNumber || defaultNumber;
console.log(currentNumber); // logs: 1

// The nullish coalescing operator avoids the above pitfall
updateNumber = 0;
currentNumber = updateNumber ?? defaultNumber;
console.log(currentNumber); // logs: 0

updateText = "";
currentText = updateText ?? defaultText;
console.log(currentText); // logs: "" // it does NOT log defaultText!

Compound assignments

Logical operators can also be compounded with assignment operator =. The short-circuit evaluation of the logical operator results in an assignment only if the left-hand side operand is truthy, falsy or nullish, depending on the logical operator.

Name Compound
assignment
Short for: Meaning
Logical AND assignment a &&= b a && (a = b) Only assigns if a is truthy.
Logical OR assignment a ||= b a || (a = b) Only assigns if a is falsy.
Logical nullish assignment a ??= b a ?? (a = b) Only assigns if a is nullish.

Generally the return value of an assignment matches the expression to the right of the =-sign. However, the return values of the logical compound assignments above are those of the logical operations without the assignment. For example, the return value of a &&= b equals the return value of a && b.


let a = 1, b = "hello";
console.log(a ||= b); // logs: 1 // logs the return value of a || b.

These compound assignments are especially useful for setting default content.


let currentNumber;
const defaultNumber = 10;

currentNumber = 0; // is falsy
console.log(currentNumber ||= defaultNumber); // logs: 10 // currentNumber was assigned the value of defaultNumber

currentNumber = 0; // is NOT nullish
console.log(currentNumber ??= defaultNumber); // logs: 0 // no assignment occurred

<body>

<label>Your name: <input id="inputName" type="text" /></label>
<button id="myButton">OK</button>
<p id="greeting">Hello <span>You</span>!</p>

<script>
  'use strict';
  const inputName = document.querySelector('#inputName'); // inputName.value will be the STRING that the user typed in the input field (i.e. the name).
  document.querySelector('#myButton').addEventListener('click', function(){
    inputName.value ||= 'John/Jane Doe'; // if no name is filled in (empty string), inputName.value gets reassigned the default value "John/Jane Doe". 
    document.querySelector('#greeting > span').textContent = inputName.value;	
  });
</script>
	
</body>

Hello You!