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.