Operators allow you to get two simple expressions and combine them to form a more complex expression.

We can classify operators based on the operands they work with. An operand is a simple expression. Some operators work with 1 operand. Most with 2 operands. Just one operator works with 3 operands.

The operator that works with 3 operands is the ternary operator, a succint way to express conditionals:

<condition> ? <expression> : <expression>

The <condition> is evaluated as a boolean, and upon the result, the operator runs the first expression (if the condition is true) or the second.

Example usage:

const running = true
(running === true) ? stop() : run()

We have the only 3-operands operator out of the way, so we can divide the rest of them in 2 parts: unary operators, and binary operators. After that, I will introduce the operator precedence concept.

Unary operators

Increment (++)

Increment a number. This is a unary operator, and if put before the number, it returns the value incremented.

If put after the number, it returns the original value, then increments it.

let x = 0
x++ //0
x //1
++x //2

Decrement (--)

Works like the increment operator, except it decrements the value.

let x = 0
x-- //0
x //-1
--x //-2

Unary negation (-)

Return the negation of the operand

let x = 2
-x //-2
x //2

Unary plus (+)

If the operand is not a number, it tries to convert it. Otherwise if the operand is already a number, it does nothing.

let x = 2
+x //2

x = '2'
+x //2

x = '2a'
+x //NaN

Logical not (!)

Invert the value of a boolean:

let value = true
!value //false

new

The new operator is used to create a new object. You follow new with the object class to create a new object of that type:

const date = new Date()

delete

delete is an operator which is not using a punctuation character, but is still an operator. It deletes a property from an object.

const car = {
  model: 'Fiesta',
  color: 'green'
}

delete car.model
delete car['color']

typeof

typeof returns a string representing the type of a variable:

typeof 'Flavio' //string
typeof 22 //number

await

await can only be used inside async functions, and it’s a way to signal JavaScript to wait for a promise to resolve. Don’t worry, we’ll talk about this extensively later.

const result = await something()

Spread operator

You can expand an array, an object or a string using the spread operator ....

Let’s start with an array example. Given

const a = [1, 2, 3]

you can create a new array using

const b = [...a, 4, 5, 6]

You can also create a copy of an array using

const c = [...a]

This works for objects as well. Clone an object with:

const newObj = { ...oldObj }

Using strings, the spread operator creates an array with each char in the string:

const hey = 'hey'
const arrayized = [...hey] // ['h', 'e', 'y']

This operator has some pretty useful applications. The most important one is the ability to use an array as function argument in a very simple way:

const f = (foo, bar) => {}
const a = [1, 2]
f(...a)

The rest element is useful when working with array destructuring:

const numbers = [1, 2, 3, 4, 5]
const [first, second, ...others] = numbers

and spread elements:

const numbers = [1, 2, 3, 4, 5]
const sum = (a, b, c, d, e) => a + b + c + d + e
const sum = sum(...numbers)

ES2018 introduces rest properties, which are the same but for objects.

Rest properties:

const { first, second, ...others } = {
  first: 1,
  second: 2,
  third: 3,
  fourth: 4,
  fifth: 5
}

first // 1
second // 2
others // { third: 3, fourth: 4, fifth: 5 }

Spread properties allow to create a new object by combining the properties of the object passed after the spread operator:

const items = { first, second, ...others }
items //{ first: 1, second: 2, third: 3, fourth: 4, fifth: 5 }

It is also the perfect way to merge two simple objects into one:

const object1 = {
  name: 'Flavio'
}

const object2 = {
  age: 35
}

const object3 = {...object1, ...object2 }

Binary operators

Addition (+)

const three = 1 + 2
const four = three + 1

The + operator also serves as string concatenation if you use strings, so pay attention:

const three = 1 + 2
three + 1 // 4
'three' + 1 // three1

Subtraction (-)

const two = 4 - 2

Division (/)

Returns the quotient of the first operator and the second:

const result = 20 / 5 //result === 4
const result = 20 / 7 //result === 2.857142857142857

If you divide by zero, JavaScript does not raise any error but returns the Infinity value (or -Infinity if the value is negative).

1 / 0 //Infinity
-1 / 0 //-Infinity

Remainder (%)

The remainder is a very useful calculation in many use cases:

const result = 20 % 5 //result === 0
const result = 20 % 7 //result === 6

A reminder by zero is always NaN, a special value that means “Not a Number”:

1 % 0 //NaN
-1 % 0 //NaN

Multiplication (*)

Multiply two numbers

1 * 2 //2
-1 * 2 //-2

Exponentiation (**)

Raise the first operand to the power second operand

1 ** 2 //1
2 ** 1 //2
2 ** 2 //4
2 ** 8 //256
8 ** 2 //64

The exponentiation operator ** is the equivalent of using Math.pow(), but brought into the language instead of being a library function.

Math.pow(4, 2) == 4 ** 2

This feature is a nice addition for math intensive JS applications.

The ** operator is standardized across many languages including Python, Ruby, MATLAB, Lua, Perl and many others.

Assignment

Use = to assign a value to a variable:

const a = 2
let b = 2
var c = 2

Comparisons

You can use the following operators to compare two numbers, or two strings. Returns a boolean:

  • < less than
  • <= minus than, or equal to
  • > greater than
  • >= greater than, or equal to
const a = 2
a >= 1 //true

When comparing strings, it checks for the letter ordering, encoded in Unicode.

Equality checks

Accepts two values, and returns a boolean:

  • == checks for equality
  • != checks for inequality
  • === checks for strict equality
  • !== checks for strict inequality

Let’s talk what we mean for strict. Without the strict check, the second operand is converted to the type of the first before making the comparison. Strict prevents this.

Examples:

const a = true

a == true //true
a === true //true

1 == 1 //true
1 == '1' //true
1 === 1 //true
1 === '1' //false

You cannot check objects for equality: two objects are never equal to each other. The only case when a check might be true is if two variables reference the same object.

Some peculiarities to be aware: NaN is always different from NaN.

NaN == NaN //false

null and undefined values are equal if compared in non-strict mode:

null == undefined //true
null === undefined //false

Logical and

Returns true if both operands are true:

<expression> && <expression>

For example:

a === true && b > 3

The cool thing about this operator is that the second expression is never executed if the first evaluates to false. Which has some practical applications, for example, to check if an object is defined before using it:

const car = { color: 'green' }
const color = car && car.color

Logical or

Returns true if at least one of the operands is true:

<expression> || <expression>

For example:

a === true || b > 3

This operator is very useful to fallback to a default value. For example:

const car = {}
const color = car.color || 'green'

makes color default to green if car.color is not defined.

instanceof

Return true if the first operand is an instance of the object passed on the right, or one of its ancestors in its prototype chain:

class Car {}
class Fiesta extends Car {}

const myCar = new Fiesta()
myCar instanceof Fiesta //true
myCar instanceof Car //true

in

Return true if the first operand is a property of the object passed on the right, or a property of one of its ancestors in its prototype chain:

class Car {
  constructor() {
    this.wheels = 4
  }
}
class Fiesta extends Car {
  constructor() {
    super()
    this.brand = 'Ford'
  }
}

const myCar = new Fiesta()
'brand' in myCar //true
'wheels' in myCar //true

Assignment shortcuts

The regular assignment operator, =, has several shortcuts for all the arithmetic operators which let you combine assignment, assigning to the first operand the result of the operations with the second operand.

They are:

  • +=: addition assignment
  • -=: subtraction assignment
  • *=: multiplication assignment
  • /=: division assignment
  • %=: remainder assignment
  • **=: exponentiation assignment

Examples:

let a = 0
a += 5 //a === 5
a -= 2 //a === 3
a *= 2 //a === 6
a /= 2 //a === 3
a %= 2 //a === 1

Precedence rules

Every complex statement will introduce precedence problems.

Take this:

const a = 1 * 2 + 5 / 2 % 2

The result is 2.5, but why? What operations are executed first, and which need to wait?

Some operations have more precedence than the others. The precedence rules are listed in this table:

Operator Description
- + ++ -- unary operators, increment and decrement
* / % multiply/divide
+ - addition/subtraction
= += -= *= /= %= **= assignments

Operations on the same level (like + and -) are executed in the order they are found

Following this table, we can solve this calculation:

const a = 1 * 2 + 5 / 2 % 2
const a = 2 + 5 / 2 % 2
const a = 2 + 2.5 % 2
const a = 2 + 0.5
const a = 2.5

I didn’t talk about bitwise operators, which are a bit too advanced for this course level, and other operators like void or other very rarely used ones.


Go to the next lesson