Blog.

JavaScript Basics

Cover Image for JavaScript Basics
Bryan Guner
Bryan Guner

Variables

Variables are declared with the var keyword. JavaScript is dynamically typed so every variable can hold a value of any data type.

Variables can be declared without an initial value.

Some example declarations:

var foo; var bar = 42; var foo, bar, baz; var foo = 42, bar = 'baz', z;

Variables that don’t explicitly get assigned an initial value have the value undefined.

ES2015

Since ES2015, let and const can be used in addition to var. We will learn how they differ from var later. For now, lets have a look how const differs from var or let: const can be assigned a value only once (constant).
Reassigning a value will either throw an error (in strict mode, see below) or is silently ignored:

const foo = 42; foo = 21; // error or ignored

consts must be initialized with a value:

const foo; // error foo = 42;

Variable names

Valid characters for variable names include a wide range of unicode characters. However, the name must start with a letter, _ or $. Not doing so will result in a syntax error.

Examples:

var π = 3.141; var _foo = π; var 0_bar = '...'; // Syntax error

Variable access

Trying to read an undeclared variable results in a runtime error:

var foo; console.log(bar); // ReferenceError: bar is not defined.

However, writing to an undeclared variable is valid by default. It will create an implicit global variable and should thus be avoided:

function foo() { bar = 42; } foo(); console.log(bar); // no error

If code runs in strict mode, assigning to an undeclared variable throws an error.

Strict mode

Strict mode is a mode of evaluating JavaScript that enforces stricter rules. It was introduced to “deprecate” certain patterns/behaviors that are considered bad or confusing.

Strict mode can be enabled for a JavaScript or a function by putting

"use strict";

at the beginning of it.

Exercise

Create a local variable with name foo and value 42. Use log(foo) to log the value of foo. Remember to open your browser’s developer tools to view the log output.

1// Create variable2​3//4log(foo);5​RunResetSubmit

Data types

JavaScript has 6 data types. Five of those are so called primitive data types:

  • Boolean
  • Number
  • String
  • Null
  • Undefined

Everything else that is not a value of one of the above types is an

  • Object

Booleans, numbers and strings

The Boolean data type has two values, true and false.

var foo = true; var bar = false;

Numbers are double precision floating point numbers, following the IEEE 754 standard

This makes it very easy to work with them, since you don’t have to differentiate between integer values and floating point values.

There are various ways that numeric value can be expressed:

var x = 5; // "integer" var y = -4.2; // "float" var z = 5e3; // = 5 * 10^3

An issue with floating point numbers is the loss of precision, which of course occurs in JavaScript as well:

0.1 + 0.2 // 0.30000000000000004

The maximum integer value that can be used without loss of precision is 253.

Math.pow(2, 53) // 9007199254740992 Math.pow(2, 53) + 1 // 9007199254740992

Strings are sequences of unicode characters and can either be delimited with a single or double quotation mark. Unlike in other languages, such as PHP, both are interpreted in the exact same way.

Example:

var foo = "bar"; var bar = 'baz';

null and undefined

JavaScript has two data types to express the absence of a value, null and undefined.

null has the only value null and undefined has the only value undefined.

The difference between those two is subtle and is best explained by how to use them:

  • undefined is the value JavaScript itself uses to indicate the absence of a value.
  • null is the value the engineer should use to indicate the absence of a value.

Examples:

var foo; // no value is assigned, foo has the value undefined var bar = null; // bar is explicitly set to null console.log(foo); // logs "undefined" console.log(bar); // logs "null"

There are other native occurrence of undefined which we will mention later.

Remember: A variable that doesn’t exist cannot be accessed at all (it will throw a reference error). Instead of saying “the variable is undefined” we rather say “the variable is not declared” to avoid confusion.

var foo; console.log(foo); // logs `undefined` console.log(bar); // reference error

Objects

Everything else besides primitive data type values is an object.

Objects are key-value stores, more specifically stringkey-value stores. The "keys" of an object are called properties.

The syntax to create a plain object is {key: value, ...}, which is called an object literal. For example:

var obj = { foo: 'bar', baz: 42 };

Note that the above example doesn’t use quotation marks around the property names. In an object literal, quotation marks can be be omitted if the property name would also be a valid variable name. If not, they need to be quoted. Number literals are valid an object literal as well.

Here are some more examples of valid and invalid property names in object literals:

var obj = { foo: 0, // valid, could be variable name 'bar': 0, // string literals are always valid 123: 0, // number literals are always valid 1.5: 0, // ^ foo-bar: 0, // invalid, would not be a valid variable name 'foo-bar': 0, // string literals are alwaus valid };

Important: No matter which value or syntax you use for a property name, the value will always be converted to a string.

ES2015

ES2015 adds two extensions to object values and object literals:

Symbols are can be used as property names. They are not converted to strings.

  • var foo = 42; var obj = { [foo]: 0, }; // creates {42: 0}

Object literals can contain computed property names:

References

Just like in Java and other object-oriented programming languages, objects are represented as references. That means if a variable has an object as a value, it really has a reference to that object.

var user = {name: 'Tom'}: ┌──────────────┐ ┌─────┬──────────┐ │ Object#123 │ │user │ ref:123 ◆┼──────▶├──────┬───────┤ └─────┴──────────┘ │ name │ "Tom" │ └──────┴───────┘

Assigning the value to another variable makes both variables point to the same object:

var owner = user; ┌─────┬──────────┐ ┌──────────────┐ │user │ ref:123 ◆┼──┐ │ Object#123 │ ├─────┼──────────┤ ├───▶├──────┬───────┤ │owner│ ref:123 ◆┼──┘ │ name │ "Tom" │ └─────┴──────────┘ └──────┴───────┘

Assigning to user.name will therefore also “change” owner.name:

user.name = 'Joe'; console.log(user.name, owner.name); // Joe, Joe┌─────┬──────────┐ ┌──────────────┐ │user │ ref:123 ◆┼──┐ │ Object#123 │ ├─────┼──────────┤ ├───▶├──────┬───────┤ │owner│ ref:123 ◆┼──┘ │ name │ "Joe" │ └─────┴──────────┘ └──────┴───────┘

But assigning a new value to either user or owner will result in only that variable referring to the new value. The other variable will still refer to the same value.

owner = {name: 'Kim'}; ┌──────────────┐ │ Object#123 │ ┌───▶├──────┬───────┤ ┌─────┬──────────┐ │ │ name │ "Joe" │ │user │ ref:123 ◆┼──┘ └──────┴───────┘ ├─────┼──────────┤ │owner│ ref:456 ◆┼──┐ ┌──────────────┐ └─────┴──────────┘ │ │ Object#456 │ └───▶├──────┬───────┤ │ name │ "Kim" │ └──────┴───────┘

The JavaScript standard defines a couple of built-in objects with additional properties and special internal behavior, must notably arrays and functions,

Prototypes (1)

You may have heard that JavaScript is a prototype-based language, unlike other languages, such as Java, which are "class-based languages".

What exactly is a prototype?

In short: A prototype is just another object. If an object A has this special connection to object B, then we say that "B is the prototype of A".

In addition to having “external” properties that can be accessed from code, objects also have internal/private properties/state. These cannot be accessed from code and their concrete implementation depends on the JavaScript engine.

Every object has an internal property [[Prototype]] (internal properties are usually denoted with [[...]] around the name). This property points to another object.

┌───────────────────────┐ ┌────────────────────────┐ │ A │ │ B │ ├───────────────┬───────┤ ├───────────────┬────────┤ │ name │ "Tom" │ ┌──▶│ toString │ <func> │ ├───────────────┼───────┤ │ ├───────────────┼────────┤ │ [[Prototype]] │ ◆───┼───┘ │ [[Prototype]] │ null │ └───────────────┴───────┘ └───────────────┴────────┘

Multiple objects can have the same prototype.

┌───────────────────────┐ ┌────────────────────────┐ │ A │ │ B │ ├───────────────┬───────┤ ├───────────────┬────────┤ │ name │ "Tom" │ ┌──▶│ toString │ <func> │ ├───────────────┼───────┤ │ ├───────────────┼────────┤ │ [[Prototype]] │ ◆───┼───┘ │ [[Prototype]] │ null │ └───────────────┴───────┘ └───────────────┴────────┘ ▲ ┌───────────────────────┐ │ │ C │ │ ├───────────────┬───────┤ │ │ time │ "day" │ │ ├───────────────┼───────┤ │ │ [[Prototype]] │ ◆───┼────────────────────┘ └───────────────┴───────┘

Since a prototype is an object, it might itself have a prototype, which may have a prototype, and so forth.

┌───────────────────┐ │ A │ ├───────────────┬───┤ │ [[Prototype]] │ ◆─┼────┐ └───────────────┴───┘ │ ▼ ┌───────────────────┐ │ B │ ├───────────────┬───┤ │ [[Prototype]] │ ◆─┼────┐ └───────────────┴───┘ │ ▼ ┌───────────────────┐ │ C │ ├───────────────┬───┤ │ [[Prototype]] │ ◆─│─ ─ ─ ▷ └───────────────┴───┘

This is called the prototype chain. Almost all objects have the same object at the end of the prototype chain, which doesn’t have a prototype itself.

An object created using literal notation will have the object Object.prototype as its prototype. You can verify this using the following commands:

var testObject = {}; Object.getPrototypeOf(testObject) === Object.prototype; // true ┌────────────────────────┐ ┌───────────────────┐ │ Object.prototype │ │ testObject │ ├───────────────┬────────┤ ├───────────────┬───┤ │ toString │ <func> │ │ [[Prototype]] │ ◆─┼──────▶├───────────────┼────────┤ └───────────────┴───┘ │ hasOwnProperty│ <func> │ ├───────────────┼────────┤ │ [[Prototype]] │ null │ └───────────────┴────────┘

In order to create an object with an object other than Object.prototype as prototype, one can use Object.create:

var a = {}; var b = Object.create(a); Object.getPrototypeOf(b) === a; // true

Prototypes (2)

Now we know what prototypes are, but not what they do or which problem they solve.

What does a prototype do?

Prototypes come in play when we are accessing the property of an object.

Whenver an object property is accessed, the object and its prototype chain are traversed until the property is found. If the end of the prototype chain is reached without finding the property, undefined is returned.

Consider the following structure:

┌───────────────────────┐ │ a │ ├───────────────┬───────┤ │ name │ "Tom" │ ├───────────────┼───────┤ │ [[Prototype]] │ ◆───┼───┐ └───────────────┴───────┘ │ ▼ ┌───────────────────────┐ │ b │ ├───────────────┬───────┤ │ name │ "Joe" │ ├───────────────┼───────┤ │ age │ 42 │ ├───────────────┼───────┤ │ [[Prototype]] │ ◆───┼───┐ └───────────────┴───────┘ │ ▼ ┌───────────────────────┐ │ c │ ├───────────────┬───────┤ │ height │ 180 │ ├───────────────┼───────┤ │ [[Prototype]] │ null │ └───────────────┴───────┘

These are the results for accessing different properties on A:

a.name // Tom `a` itself has this property, it shadows `b.name` a.age // 42 `a`'s prototype has this property a.height // 180 `a`'s prototype's prototype has this property a.eyeColor // undefined this property doesn't exist

This is also the reason why we can access .toString() on almost every object: It is defined in Object.prototype, which sits at the end of every prototype chain.

var user = {name: 'Tom'}; user.toString() // "[object Object]"

Note: Assignments to properties will (almost) always create or update a property on object itself, even if a property with the same name already exists in the prototype chain. The property in the prototype chain is then shadowed, similar to variable shadowing in scopes.

Built-in objects: Arrays and functions

Arrays

Arrays are objects, which treat properties with numeric keys (i.e. 0, 1, 2, …) in a special way. For all purposes, they behave like arrays in other languages.

JavaScript has a special syntax for creating arrays, [value, value, ...]:

var arr = [1, 2];

If you run console.dir([1, 2]) in your browser’s console, you can inspect the structure of the array object in more detail.

Unlike “plain” objects, array objects have Array.prototype as prototype, which provides all the array methods, such as .push, .map, etc.

┌──────────┐ [[Prototype]] ┌──────────────────┐ │ obj {} │───────────────────────────▶│ Object.prototype │ └──────────┘ └──────────────────┘ ▲ │ [[Prototype]] │ ┌──────────┐ [[Prototype]] ┌─────────────────┐ │ │ arr [] │───────────────▶│ Array.prototype │───┘ └──────────┘ └─────────────────┘

Functions

Functions are the only kind of objects that are callable, and JavaScript also has a special syntax for defining them:

function foo() { console.log("I'm a function"); }

There are other ways to create functions, which will be explained later.

The most important implication of functions being objects is that “work” just like any other value. Functions can be passed to functions and returned from functions, allowing to creation of higher-order functions.

Similar to arrays, function objects also have a dedicated prototype, Function.prototype:

┌──────────┐ [[Prototype]] ┌──────────────────┐ │ obj {} │───────────────────────────────▶│ Object.prototype │ └──────────┘ └──────────────────┘ ▲ ▲ │ │ [[Prototype]] │ ┌──────────┐ [[Prototype]] ┌─────────────────┐ │ │ │ arr [] │───────────────▶│ Array.prototype │─┘ │ └──────────┘ └─────────────────┘ [[Prototype]] │ │ ┌──────────────────┐ [[Prototype]] ┌──────────────────┐ │ │func function(){} │───────────────▶│Function.prototype│──┘ └──────────────────┘ └──────────────────┘

Operators

Like many C-like programming languages, most operators JavaScript are binary or unary, and written in infix notation, i.e. a op b.

Here is list of typical operations:

Assignment: a = b, a += b, a |= b, and more

Arithmetic: a + b, a - b, a * b, a / b, a % b, +a, -a

String concatenation: a + b

Boolean: a && b, a || b, !a

Bitwise: a & b, a | b, a ^ b, ~a, a << b, a >> b, a >>> b

Function calls: foo(), foo(a, b, c)

Increment/Decrement: a++, ++a, a--, --a

Conditional: foo ? bar : baz

Others: in, instanceof, typeof, new

JavaScript also has comparison operators and property accessors, both of which are explained in more detail in t

Demo

Run the code and have a look at the output. Is it what you expect? Try some other values / operators and look at the output (remember to open the console).

1var a = 10;2var b = 5;3var c = "1";4​5log(a + b);6log(a + c);7log(b + Number(c));8​Run

Comparison operators

As already established at the beginning, JavaScript is dynamically typed. It also performs type conversion, if a specific data type is expected and not provided. For example in a * b, the values of a and b will be converted to numbers first.

Even though there are well defined rules for converting one data type into another, these rules can be quite surprising.

What does this have to do with comparison? JavaScript has two kind of comparison operators:

  • Loose comparison (a == b, a != b)
  • Strict comparison (a === b, a !== b)

The difference is that loose comparison will convert both values to the same data type if they are of different data types. Strict comparison immediately returns false if both values don’t have the same type.

Examples:

"42" == 42 // true "42" === 42 // false // Objects are compared by reference [1,2] == [1,2] // false [1,2] === [1,2] // false

The following tool visualizes the steps of the abstract equality comparison algorithm, which is used for loose comparison.

You can select some predefined examples and see which steps are performed during the comparison. The results will probably surprise you. You can also provide your own values.

Select an example...[1,2] == '1,2'[0] == false'\n' == false'0XA19' == 2585

==

Compare

Also have a look at this table to get a quick overview of the differences between == and ===.

The above examples hopefully showed you that loose comparison isn’t that "simple" and it’s not always clear what ends up being compared in the end. For that reason you should follow this advice:

You should always use strict comparison, unless you explicitly want to make use of the type conversion (i.e. you know what you are doing).

If you write an API, make it clear which data type it expects (e.g. through comments).

Property access

Properties of objects can be accessed in two ways:

  • Dot notation (obj.prop)
  • Bracket notation (obj["prop"])

You should always prefer dot notation, unless you have to use bracket notation. This could be if the property name is not a valid identifier or if it comes from a variable. You can use any expression inside the brackets.

Examples:

obj['test-field']; // test-field is not a valid identifier var field = 'test'; obj[field]; obj['example' + i];

Because you can only use dot notation if the property name is a valid identifier name, array objects can only be accessed via bracket notation, arr[0], not dot notation, a.0.

You can assign to properties by putting the member expression on the left hand side of an assignment expression:

obj.prop = value;

If you have nested objects/arrays, you simply use a valid property accessor repeatedly:

var obj = {foo: {bar: [42, 21]}}; console.log(obj.foo.bar[0]); // which is evaluated as ((obj.foo).bar)[0]

Accessing a non existing property does not throw an error, it returns undefined:

var obj = {}; console.log(obj.foo);

Control structures

JavaScript provides the same control structures known from other C-like languages:

if (...) { ... } else if (...) { ... } else { ... }

while (...) { ... } and do { ... } while (...)

for (...; ...; ...) { ... }

switch (...) { case ...: ... }

Additionally, JavaScript provides the for...in loop to iterate over properties of objects:

for (var prop in obj) { console.log(prop, obj[prop]); }

prop is a variable containing the property name. You can use bracket notation to access the property values.

ES2015

ES2015 introduces for/of statements for iterating over iterables:

var arr = [1,2,3]; for (var v of arr) { console.log(v); } // 1 // 2 // 3

Function definitions

There are two syntactic constructs to create functions: function declaration and function expressions.

Function declarations start with the function keyword followed by a name, the parameter list and the function body:

function foo(a, b, c) { // do something }

Function expressions have the same structure, but their name is optional:

var foo = function(a, b, c) { // do something };

Note: Since functions are objects, they can be treated like any other value. They can be assigned to variables, passed to other functions and returned from functions. The code above is just an assignment expression with a function as value.

All function objects created either way behave exactly the same. Whether the parser treats a function definition as declaration or expression depends on where the definition is placed. If it is an expression context, it is interpreted as an expression, otherwise as a declaration. That’s why

function () { }

generates an error (function declaration without name), but

(function () { })

does not, because the grouping operator ((...)) can only contain expressions.

Function Calls

Like other C-like languages, functions are called by putting () after the function reference:

myFunction()

Unlike other languages, functions can be called with any number of arguments, no matter how many formal parameters they have:

function myFunction(foo, bar) { console.log(foo, bar); } myFunction(); // undefined undefined myFunction(1); // 1 undefined myFunction(1, 2); // 1 2 myFunction(1, 2, 3); // 1 2

Each function has access to the special arguments variable, which is an array-like value. This allows you to access all the arguments passed to a function, even if there are more than formal parameters:

function myFunction(foo, bar) { console.log(foo, bar, arguments); } myFunction(); // undefined undefined [] myFunction(1); // 1 undefined [1] myFunction(1, 2); // 1 2 [1, 2] myFunction(1, 2, 3); // 1 2 [1, 2, 3]

Scope

Unlike other programming languages, JavaScript only has function scope, not block scope. In the following example, all variables are visible throughout the function:

function foo() { var bar = 42; // loop for (var i = 0; i < 10; i++) { var j = i; } console.log(bar); // 42 console.log(i); // 10 console.log(j); // 9 }

In other languages, like Java, the variables i or j would not be available where the above code tries to access them.

ES2015

The big difference between let, const, and var is that let and const are block scoped. If we would use let instead of var in the above example, we would get the following result:

function foo() { let bar = 42; // or var or const, doesn't matter // loop for (let i = 0; i < 10; i++) { // i is scoped to the loop body block let j = i; // j is scoped to the loop body block } console.log(bar); // 42 console.log(i); // ReferenceError console.log(j); // ReferneceError }

Hoisting

This behavior becomes more understandable after we introduce hoisting. Before a JavaScript function is even executed, the engine finds all variable and function declarations and creates a binding for them in the functions scope.

Thus, the example of the previous slide is equivalent to the following:

function foo() { var bar, i, j; bar = 42; // loop for (i = 0; i < 10; i++) { j = i; } console.log(bar); // 42 console.log(i); // 10 console.log(j); // 9 }

Note how all variable declarations are put at the top of the function. The value will still be assigned only when the execution reaches the line of the assignment expression.

One of the practical implications of the hoisting concept is mutually recursive functions. Consider the following example:

function isEven(n) { if (n == 0) { return true; } return isOdd(n - 1); } // Normally may call `isEven` even though it's mutually // dependent on the `isOdd` which is defined below. console.log(isEven(2)); // true function isOdd(n) { if (n == 0) { return false; } return isEven(n - 1); }

Closures

Wikipedia describes closures as:

In programming languages, a closure (also lexical closure or function closure) is a function or reference to a function together with a referencing environment — a table storing a reference to each of the non-local variables (also called free variables or upvalues) of that function.

According to this definition, every function in JavaScript is a closure, because every function has an (internal) reference to the environment it was created in. The simplest example is:

var foo = 42; function bar() { console.log(foo); }

Here the function bar has access to foo, which is defined outside of it.

Important: The value of a free variable is determined when the function is executed, not when the function is defined. Read the previous sentence three times to make sure you really understand what it implies.

What is this?

this is a special “variable” which implicitly exists in every function. It can be thought of being similar to Java’s this and Python’s self, but it’s much more flexible than that.

Important: The value of this is determined when the function is called, not when the function is defined.

Given the following function:

function foo() { console.log(this); }

these would be the values of this if called in those specific ways:

// "normal call": global object / window in browsers // undefined in strict mode foo(); // as object "method": to the object var obj = {method: foo}; obj.method(); // via .call / .apply: To the value passed as first argument foo.call(bar);

Comments:


    More Stories

    Cover Image for Save Content

    Save Content

    Bryan Guner
    Bryan Guner