JavaScript in a Nutshell
I recently came across Dan Abramov’s "What is JavaScript Made Of" blog post. It contains a wealth of knowledge about the building blocks of JavaScript. Below, is my own adaptation with additions and omissions where I deem appropriate. Most additions stem from the site eloquent JavaScript or the book JavaScript: The Definitive Guide. The summary below has helped me to understand the intricacies of JS a little better and I hope it helps you too.
Value
The smallest fundamental unit that a computer can process and store is a binary digit (bit). Similar to a light switch, a bit takes one of two possible states – hence the Latin prefix “bi” which means “2”. All data is composed of long sequences of bits which play different roles. To be able to work with these bit sequences, it helps to bundle them in a sensible way. In the JavaScript (JS) world, these bundled bit sequences are called values. Values can play different roles that are characterised by the value’s type. The basic value types (aka. primitive values) in JS are numbers, strings, bools, objects, functions, and undefined values.
Type of Value
Above I mentioned the basic types of values. JS provides a convenient way to determine the type of a value. Simply prepend typeof
to
find a value’s type, so console.log(typeof 42)
will print the type number
to the console.
Empty Values
null
and undefined
are two special values. They are quite unpopular because they often cause errors. The difference between
the two is subtle. A declared variable with unassigned value is undefined
; whereas, null
is a value that can be assigned to a variable to represent no value.
Most of the time the two are treated interchangeably and the difference doesn’t matter.
Variable
A variable is a “named storage” for data – it’s not the value itself. Creating a variable is called “declaring” a variable. After
declaration, the variable is undefined
. If, for example, we write let number = 50
, we can now write number
instead of the value 50
throughout
the code. Variables declared with let
can be reassigned, whereas ones declared with const
can’t. Since we declared number
with let
above,
we can reassign it as follows: number = 63
, the variable now stores (or, points to) a new value. It’s tempting to think that the original
value of 50
has changed to 63
. It hasn’t! Instead, after reassignment, the number 50
still exists in memory. But it’s no longer accessible
via the variable number. If there are no other references to the value 50
, it becomes eligible for garbage collection.
Equality
Two values are equal if they are the same value. JS distinguishes between strict equality and loose equality. For strict equality,
we use three equal signs ===
. Strict equality is equality as defined in the first sentence of this paragraph. For example, 5 === 5
and "Hello World" === "Hello World"
.
But sometimes it helps if different values that look the same are considered equal, like the string “2”
and the number 2
. That’s where we use
loose equality which uses two equal signs ==
, so "2" == 2
is true but "2" === 2
is false
.
Assignment
Since we just looked at ===
and ==
, it only makes sense to cover =
next. A single equals sign is used to assign a value to a
variable. Example: greet = "Hello World"
.
Literal
If you refer to a value by writing it out in your code, you are using a literal. For example, 10
is a number literal, and “Hello”
is a string literal. The literal is literally writing the value without the use of a variable.
Scope
The parts of a code-base in which a variable is accessible is called the variable’s scope.
What’s interesting is that before JS ES6 (2015) variables only had global and function scope.
ES6 introduced the keywords let
and const
to provide block scope. Let’s unravel this in more detail below.
Variables declared in a {...}
block can’t be accessed from outside the block:
{
const x = 1;
let y = 2;
}
// x and y can't be used here
Variables declared using var
can’t have block scope which leads to confusing scoping, so best avoid it’s use completely:
{
var x = 3;
}
// x can be used here
Variables inside a function are not accessible from outside the function, irrespective of whether they were declared with var
, let
, or const
:
function myFunction() {
var x = 1;
const y = 2;
let z = 3;
}
// x, y, and z can't be used here
Statement
JavaScript code is just a sequence of statements that are separated by semicolons. By default, statements are executed in the order they are written
one after the other. Sometimes you’ll want to change the order of execution and JS offers some tools to do so, e.g., conditionals (if
, switch
), loops (while
, for
),
or jumps (break
, return
, throw
). To get a feel for statements, consider the following examples:
window.close();
greeting = “Hello” +” ” + name;
let x;
let y = 10;
const x, y, z;
Object
In JS, objects belong to the list of primitive values. And they store a collection of properties and values. For example, the
object {brand:"Audi", model:"Q8"}
has a property brand that stores the value "Audi". It helps to think of an object as a container, where
the properties are labels, and the values are the contents of the container. Sometimes you’ll hear people say that an object is an instance
of a class. It’s true, but it doesn’t help if you don’t know what a class is. We’ll cover that further below.
Property
Variables and properties are similar because they store values. Unlike variables, properties are contained within objects.
Object Literal
An object literal is when you literally write out an object, e.g.,{flavour: "chocolate"}
.
Dot Notation
Dot notation is used to read the property of an object. Consider the object car = {brand:"Audi", model:"Q8"}
where "Audi" can be read by writing car.brand and "Q8" by writing car.model.
Bracket Notation
Bracket Notation is used when you want to read the property of an object without specifying the property name. That might be the case when you create a loop over the object’s properties:
for (let key in car) {
console.log(car[key]);
}
In the example, car[key]
uses the bracket []
notation.
Classes
Often it’s useful to define a class of objects that share common properties. To understand why, first take a look at the following class:
class Car {
constructor(brand, model, year) {
this.brand = make;
this.model = model;
this.color = year;
}
accelerate() {...}
brake() {...}
}
Although the values (e.g., Audi, Q8, Back) will be different from one car to the next, most car’s do have similar properties
(e.g., a brand, model, and color) and similar behaviour (e.g., they accelerate, and brake). The class
provides a blueprint for every
car (aka. instance) of the class
. Perhaps the most significant advantage is that a class
groups properties and methods that can be used across all member objects.
this
The keyword this
often holds the reference to an object. You can think of it as a special hidden argument that is passed into
a function by default. It’s value depends on how the function is called. Often, this points to the object that owns the method being
executed. To have more control over the value of this
, helpers, e.g., .bind
, are used.
Prototype
Every JavaScript object has a second object associated with it, known as its prototype. All JavaScript objects inherit properties
and methods from their associated prototype. This is true for both standard JavaScript objects (e.g., Array) and custom objects you create
(e.g., Car, Person, Student etc.). Using the class Car
from earlier, imagine you call a function on an instance of Car, my_car
, that doesn’t
exist, e.g., my_car.steer()
. Since there’s no steer()
function on my_car
, JavaScript will look for that method in its prototype, next on that
object’s prototype, and so on. If it reaches the end of this prototype chain without finding that method, we get undefined
. This also explains
why the my_car
object has a toString
method that was not defined in the class Car
. It comes from the prototype.
Array
An array is an object that presents an ordered collection of values (aka. elements). In simple terms: it allows us to list stuff. Every
element in an array has an index, where the first starts with 0.
Fun fact: the highest possible index is 2³²-2 – that’s over 4 billion.
It helps to think of arrays as specialized objects that exist to prevent us from having to write something like {0: ..., 1: ..., 2: ...}
.
Function
A block of JS code that is defined once but can be executed (aka. called, invoked) any number of times. In JS, functions are objects
(and, therefore, values). They are helpful if you want to do something without repeating yourself. For example, function greet(name){return "Hello" + " " + name}
allows you to write a greeting text without having to explicitly write the entire text every time. In other words, instead of writing "Hello John"
,
"Hello Peter"
and "Hello Mary"
, we can write greet("John")
, greet("Peter")
and greet("Mary")
.
Arguments or Parameters
Consider the example function greet(name){...}
and the call greet("Peter")
. Here, name is an argument and "Peter"
is a parameter.
Most of the time these two terms are used interchangeably.
Function Expression
JavaScript allows us to set a variable equal to a function: let greet = function(name) {...}
. The stuff that comes after the = is
called the function expression. To call this function, write greet("Peter")
.
Function Declaration
Above, in the function expression, I wrote: let greet = function(name) {...}
. A shorter version of this is function greet(name) {...}
.
Here, the name “greet”
is placed after function
. This is called a function declaration.
Arrow Functions
The syntax of an arrow function is similar to a function expression: let greet = (name) => {...}
. The arrow =>
separates the parameters
from the body of the function. A notable characteristic of arrow functions is that they don’t have their own this context. Instead,
they inherit the this value from their surrounding context.
Et voila!
The concepts above only cover a small part of the JavaScript universe. Feel free to use this glossary whenever you feel the need to refresh the core concepts of JavaScript.