Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1148 lines (790 sloc) 28.5 KB

JavaScript

Learn the basics of JavaScript, a high-level, dynamic, untyped and interpreted programming language, and one of the three core technologies of the web.

You will need

  • Google Chrome (recommended, any browser with developer tools will do)

What is JavaScript?

JavaScript is a high-level, dynamic, untyped, and interpreted programming language. Alongside HTML and CSS, JavaScript is one of the three core technologies of World Wide Web.

JavaScript is prototype-based with first-class functions, making it a multi-paradigm language, supporting object-oriented, imperative, and functional programming styles.

JavaScript is still evolving

It has been standardized in the ECMAScript language specification.

JavaScript ES6+ support

There are features from ES6 that are not yet fully supported on all browsers.

In client-side code running in the browser, you should stick with ES5 or use a JavaScript compiler like Babel or a module bundler like Webpack to turn your ES6+ code into compatible ES5 code before releasing it.

In server-side code running with the latest Node.js versions, all ES6+ features are supported except for imports.

JavaScript types

JavaScript has 6 primitive data types

let aString = "HEIG-VD";
let aNumber = 3.12;
let aBoolean = true;
let nullValue = null;
let undefinedValue;
let aSymbol = Symbol('foo');

console.log(typeof aString); // "string"
console.log(typeof aNumber); // "number"
console.log(typeof aBoolean); // "boolean"
console.log(typeof nullValue); // "object"
console.log(typeof undefinedValue); // "undefined"
console.log(typeof aSymbol); // "symbol"

// There is no integer type
console.log(Number.isInteger(aNumber)); // false
console.log(typeof 4); // "number"
console.log(Number.isInteger(4)); // true

// Symbols are unique identifiers
console.log(Symbol('foo') == aSymbol); // false

The types are:

  • String
  • Number
  • Boolean
  • Null
  • Undefined
  • Symbol (ES6)

Note that null is a type, but typeof null === object. This is a remnant from the first version of JavaScript.

JavaScript has dynamic objects

// Let's create an object
let person = {
  firstName: 'John',
  lastName: 'Doe'
};

// We can dynamically add properties
person.gender = 'male';

let property = 'zip';
person[property] = 1446;

// And delete them
delete person.firstName;

// And list them
for (const key in person) {
  console.log(key + ': ' + person[key]);
}
lastName: John
gender: male
zip: 1446

Objects have no class, they are dynamic bags of properties.

Every object has a different list of properties.

Array are objects

They are list-like objects with numeric keys.

// Let's create an array
let fruits = [ 'apple', 'pear' ];

console.log(typeof fruits); // "object"

// Iterate over it
for (let i = 0; i < fruits.length; i++) {
  console.log('fruit ' + i + ' is ' + fruits[i]);
}

// fruit 0 is apple
// fruit 1 is banana

We'll learn more about arrays later.

JavaScript is untyped

Values have a type, but variables don't. When you declare a variable, you don't specify a type.

let aVariable = "aString";
console.log(typeof aVariable); // "string"

aVariable = 3.12;
console.log(typeof aVariable); // "number"

aVariable = true;
console.log(typeof aVariable); // "boolean"

aVariable = [ 1, 2, 3 ];
console.log(typeof aVariable); // "object"

aVariable = {
  aProperty: "aValue"
};
console.log(typeof aVariable); // "object"

The type can change over time.

Comparing values with == or ===

The == operator loosely compares values for equality:

console.log(1 == true); // true
console.log(2.3 == "2.3"); // true
console.log(false == []); // true

The === operator strictly compares for equality:

console.log(1 === true); // false
console.log(2.3 === "2.3"); // false
console.log(false === []); // false
console.log(42 === 42); // true

Falsy values

The following values all evaluate to false: false, 0, "", null, undefined, NaN.

if (0) {
  console.log('Zero is truthy');
} else {
  console.log('Zero is falsy'); // "Zero is falsy"
}

This can cause weird bugs sometimes:

let countdown = "";
if (countdown == 0) {
  console.log('We are done'); // "We are done"
} else {
  console.log('We are not done');
}

Therefore, it's recommended to always use the triple-equal === operator for equality comparisons.

JavaScript supports first-class functions

"A programming language is said to have first-class functions if it treats functions as first-class citizens.

Specifically, this means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures."

Storing functions in variables or data structures

A JavaScript function isn't a special construct linked to a class like in Java. It can be stored in variables like any other value.

// Store a function in a variable
let hello = function(name) {
  console.log('Hello ' + name + '!');
};

// The hello variable now holds a function
console.log(typeof(hello)); // "function"

// You can call it
hello('World'); // "Hello World!"

// Store a function as an object's property
let anObject = {
  aProperty: function() {
    return 42;
  }
};

// That property now holds a function as its value
console.log(typeof(anObject.aProperty)); // "function"

let value = anObject.aProperty();
console.log(value); // 42

Returning functions from a function

// Let's define a function that returns a function
function makeSquareFunction() {
  return function(n) {
    return n * n;
  };
}

// By calling it, we get a function
let square = makeSquareFunction();
console.log(typeof(square)); // "function"

let result = square(5);
console.log(result); // 25

Note that functions can be anonymous (i.e. they have no name), like the function returned from makeSquareFunction:

return function(n) {
  return n * n;
};

Passing functions as arguments

A function can take another function as an argument.

function hello(name) {
  console.log('Hello ' + name + '!');
}

function callIt(func) {
  func('World');
}

callIt(hello); // "Hello World!"

Function as argument exercise

// Let's define a couple of arithmetic function
function add(a, b) {
  return a + b;
}
function multiply(a, b) {
  return a * b;
}

// Define a function that takes two numbers
// and a function to apply to those numbers
function compute() {
  // Give me some arguments and implement me!
}

// Call compute with "add"
let value = compute(2, 4, add);
console.log(value); // 6

// Call compute with "multiply"
value = compute(2, 4, multiply);
console.log(value); // 8

Open the RunKit and try to implement it!

Transforming data with functions

These properties of functions enable powerful functional programming patterns:

// Define an array of people objects
let people = [
  { firstName: 'John', lastName: 'Doe' },
  { firstName: 'John', lastName: 'Smith' },
  { firstName: 'Deborah', lastName: 'Smith' }
];

// Define a function that takes a person and returns their last name
function getName(person) {
  return person.lastName;
}

// The "map" function of arrays returns an array of the same size,
// but with each element "mapped" or "transformed" using the provided
// function
let lastNames = people.map(getName);

// We transformed an array of people into an array of last names
console.log(lastNames); // [ "Doe", "Smith", "Smith" ]

Arrow functions (ES6+)

While seaching for examples on the web, you will stumble upon a strange syntax:

let divideFunc = (nb1, nb2) => nb1 / nb2;

You are facing the new ES6 syntax for functions called arrow functions. The example above is (mostly) equivalent to writing:

let divideFunc = function(nb1, nb2) {
  return nb1 / nb2;
};

Arrow function arguments

Let's see how an arrow function is written:

`(nb1, nb2)` => nb1 / nb2

The part left of the => represents the function's arguments.

If your function has only one argument, you can omit the parentheses:

let squareroot = `number` => Math.sqrt(number);

But if your function has no arguments, you MUST add empty parentheses:

// No argument
let callback = `()` => console.log("Timeout finished");
setTimeout(callback, 1000);

Body of arrow functions

(nb1, nb2) => `nb1 / nb2`

The part right of the => is the body of the function; note the absence of brackets ({}).

The return keyword is implicit with one-line bodies that have no brackets:

// This arrow function will return the square root of the number
let squareroot = number => `Math.sqrt(number)`;
console.log(squareroot(4)); // 2

If the body has more than one line, you MUST add brackets {} around it (and use the return keyword if necessary):

let square = number => `{`
  `let result = number * number;`
  `return result;`
`}`;

console.log(square(5)); // 25

Constructors

Though JavaScript doesn't really have classes (until ES6), any function can behave like a constructor and create an object.

For a function to act as a constructor, you don't have to declare it differently than any other function. All you have to do is call the function with new like in most object-oriented languages:

function Starship() {
}

let discovery = new Starship();
console.log(discovery); // {}
console.log(discovery instanceof Starship); // true

The discovery variable stores a new (and empty) object, of type Starship.

Note that there's nothing special about this function: calling it with new is what makes it a constructor. It's simply a convention to put the first letter in uppercase.

The this keyword

Calling a constructor function with new give you access to this in its body. That variable refers to the object that is being created.

You can modify this object, for example to attach values you receive from arguments to it:

function Starship(name, designation) {
* this.name = name;
* this.designation = designation;
}

let discovery = new Starship("Discovery", "NCC-1031");
console.log(discovery);
// Starship {name: "Discovery", designation: "NCC-1031"}

It's possible to implement class-like structures with constructor functions and prototypes. JavaScript ES6 also adds actual classes (based on prototypes). But that's outside the scope of this tutorial.

Variables

Defining variables

There are three ways to define a variable in JavaScript:

// ES5
var aString = 'foo';

// ES6
let aNumber = 42;
const aBoolean = true;

Note that var always works, but let and const are only available in ES6 and later versions.

Dynamic or constant variables

Variables declared with var or let are dynamic. Their value can change over time.

var aString = 'foo';
let aNumber = 24;

console.log(aString); // "foo"
console.log(aNumber); // 24

aString = 'bar';
aNumber = 25;

console.log(aString); // "bar"
console.log(aNumber); // 25

Variables declared with const cannot change. They are constants:

const theMeaningOfLife = 42;

theMeaningOfLife = 43; // TypeError: Assignment to constant variable.

The function scope

Variables declared with var in a function are visible everywhere in that function. Note that they are NOT block-scoped like in most languages.

function logThings(things) {

  var numberOfThings = things.length;

  for (var i = 0; i < numberOfThings; i++) {
    var thing = things[i];
    console.log(thing);
  }

  console.log('Number of things: ' + numberOfThings);
  console.log('Last thing: ' + thing);
  console.log('Iterator: ' + i);
}

logThings([ 'apple', 'banana', 'pear' ]);

// "apple"
// "banana"
// "pear"
// "Number of things: 3"
// "Last thing: pear"
// "Iterator: 3"

The block scope

The let and const keywords introduced in ES6 create block-scoped variables, only visible in the block, statement or expression on which they are used.

function logThings(things) {

  const numberOfThings = things.length;

  for (let i = 0; i < numberOfThings; i++) {
    let thing = things[i];
    console.log(thing);
  }

  console.log('Number of things: ' + numberOfThings);
  console.log('Last thing: ' + thing);
}

logThings([ 'apple', 'banana', 'pear' ]);

// "apple"
// "banana"
// "pear"
// "Number of things: 3"
// ReferenceError: thing is not defined

It is recommended to use them in ES6-compatible environments.

The (evil) global scope

Variables declared with var outside of any function are global variables, accessible anywhere.

// A global variable
var name = 'World';

function hello() {

  // We can use "name" even though it's not an argument
  // of the function, because it's global
  console.log('Hello ' + name + '!');

  // It's a bad idea to use them because anyone can
  // change their value and mess up your program
  name = 'Bob';
}

hello(); // "Hello World!"
hello(); // "Hello Bob!"

You should almost never use them.

When it's okay to use the global scope

In an HTML page, all loaded scripts share the same global scope.

In that context, ES5 libraries expose global variables so that your code can use them. For example, jQuery provides the $ global variable for easy access.

In a Node.js script, the global scope is limited to the file you're in, so it's okay to use it.

If you're not writing either one of those, just don't use global variables.

Oops, global scope

If you forget the var, let or const keyword, JavaScript will not complain. It will simply consider the variable global.

// Let's declare a global variable
var i = 42;

// And a function that logs each thing in the passed array
function logThings(things) {
  // Oops, we forgot the "var" or "let"
* for (i = 0; i < things.length; i++) {
    console.log(things[i]);
  }
}

var fruits = [ 'apple', 'banana', 'pear' ];
logThings(fruits);

// Oops, we've modified something outside of the function
console.log(i); // 3

Just don't do it.

String syntax

In JavaScript, you (now) have 3 ways to use strings:

// With single quotes: '
let string = 'I\'m your "Wurst" nightmare: ' + worstNightmare;

You have to escape all other single quotes, and use + to concatenate.

// With double quotes: "
let string = "I'm your \"Wurst\" nightmare: " + worstNightmare;

You have to escape all other double quotes, and use + to concatenate.

ES6 also adds the new template literals:

// With backticks (template literals): `
let string = \`I'm your "Wurst" nightmare: ${worstNightmare}`;

You have to escape all other backticks, but you can use single and double quotes without escaping. To insert variables inside the string, use ${variable}. (To do a back-tick use Shift-^, then hit the Space bar.)

Manipulating arrays

Arrays in JavaScript are objects and provide you with a boatload of methods to manipulate items:

Function Effect
.forEach() Calls a function for every element in the array
.concat() Concatenates two arrays into one, and returns this new array
.find() Finds the first element that passes a provided test function
.pop() Removes the last element, and returns it (.shift() does the same but for the first element)
.push() Adds new elements to the end of an array (.unshift() does the same but adds them to the beginning of the array)
.slice() Returns a portion of the array
.reverse() Reverses the order of the elements in an array (this modifies the original array)

Examples

.forEach()

let crew = ["Jonathan", "T'Pol", "Trip", "Malcolm", "Sato", "Travis"];
crew.forEach(function(element, index) {
  console.log("Hello, my name is " + element + ", and I'm n°" + index);
});

.find()

let ages = [3, 10, 19, 25];
let adult = ages.find(function(age) {
  return age >= 18;
});
console.log(adult); // 19

.slice()

let starships = ["NX-01", "NCC-1701", "NCC-1701 D", "NCC-1764", "NCC-74656"];
// Start at position 0, included, and end before position 3, excluded.
let enterprises = starships.slice(0, 3);
console.log(enterprises); // ["NX-01", "NCC-1701", "NCC-1701 D"]

The for...of loop

ES6's for...of loop is a new, simpler way of iterating over all elements of an array:

let fruits = [ 'apple', 'pear' ];

// Classic "for" loop
for (let i = 0; i < fruits.length; i++) {
  let fruit = fruits[i];
  console.log('fruit: ' + fruit);
}

// Equivalent "for...of" loop
for (let fruit of fruits) {
  console.log('fruit: ' + fruit);
}

The for...of loop is actually not limited to arrays: it can iterate over any iterable object such as Map, Set, etc.

Destructuring assignment

The destructuring assignment syntax makes it possible to unpack values from arrays, or properties from objects, into distinct variables.

Array destructuring

Basic variable assignment.

let foo = [ 'one', 'two', 'three' ];

*let [ one, two, three ] = foo;
console.log(one); // "one"
console.log(two); // "two"
console.log(three); // "three"

Equivalent without destructuring.

let foo = [ 'one', 'two', 'three' ];

*let one = foo[0];
*let two = foo[1];
*let three = foo[2];
console.log(one); // "one"
console.log(two); // "two"
console.log(three); // "three"

You can also use destructuring separately from the variables' declaration:

let foo = [ 'one', 'two', 'three' ];
let one, two, three;

*[ one, two, three ] = foo;
console.log(one); // "one"
console.log(two); // "two"
console.log(three); // "three"

Array destructuring features & tricks

Here's a few things you can do with array destructuring:

// Default values
let a, b;
*[ a=5, b=7 ] = [ 1 ];
console.log(a); // 1
console.log(b); // 7

// Swapping variables
let c = 1;
let d = 3;
*[c, d] = [d, c];
console.log(c); // 3
console.log(d); // 1

// Ignoring values
*let [ e, f ] = [ 'foo', 'bar', 'baz' ];
console.log(e); // "foo"
console.log(f); // "bar"

// Assigning the rest to a variable
*let [ g, ...h ] = [ 1, 2, 3 ];
console.log(g); // 1
console.log(h); // [2, 3]

Object destructuring

Basic variable assignment.

let o = { p: 42, q: true };

*let { p, q } = o;
console.log(p); // 42
console.log(q); // true

Equivalent without destructuring.

let o = { p: 42, q: true };

*let p = o.p;
*let q = o.q;
console.log(p); // 42
console.log(q); // true

You can also use destructuring separately from the variables' declaration (but you have to put it within parentheses):

let o = { p: 42, q: true };
let p, q;

*({ p, q } = o);
console.log(p); // 42
console.log(q); // true

Object destructuring features & tricks

Here's a few things you can do with object destructuring:

// Assigning to new variable names
let o = { p: 42, q: true };

*let { p: foo, q: bar } = o;
console.log(foo); // 42
console.log(bar); // true

// Default values
*let { a = 10, b = 5 } = { a: 3 };
console.log(a); // 3
console.log(b); // 5

// Unpacking fields from function parameters
*function userId({id}) {
  return id;
}

let user = { id: 42, name: 'Bob' };
console.log('userId: ' + userId(user)); // "userId: 42"

JSON

JSON who?

JSON stands for JavaScript Object Notation. It is a syntax that is used to represent JavaScript objects with text.

JSON can only describe the following types:

Types Notation
String "text"
Number 2
Boolean true, false
Null null
Array [ "value1", "value2" ]
Object { "property1": "value1", "property2": "value2" }

Object properties and strings MUST be double-quoted.

Note that you cannot put a JavaScript function in a JSON object.

Example

Here is an example of a JavaScript object, and its description in JSON:

let starship = {
  designation: "NX-01",
  crew: 83,
  captain: {
    firstname: "Jonathan",
    lastname: "Archer",
    activeService: true
  },
  species: [
    "human",
    "dog",
    "denobulan",
    "vulcan"
  ],
* "warp.factor": 5,
* "cloak": null
};

This is a JavaScript object. You can put double quotes around property names, but you don't have to unless it's an invalid identifier (e.g. cannot use . in a variable name).

{
  "designation": "NX-01",
  "crew": 83,
  "captain": {
    "firstname": "Jonathan",
    "lastname": "Archer",
    "activeService": true
  },
  "species": [
    "human",
    "dog",
    "denobulan",
    "vulcan"
  ],
  "warp.factor": 5,
  "cloak": null
}

This is JSON. The double quotes around property names are required.

Using JSON

Manually declaring a JavaScript object in JSON (or the opposite) can be quite tedious, especially with deep, complex objects.

Fortunately, JavaScript provides the global JSON object which can do it for you. To transform a JavaScript object to JSON text, use JSON.stringify():

let crew = {name: "T'Pol", species: "Vulcan", station: "Science Officer"};
*let crewJson = JSON.stringify(crew);
console.log(crewJson);
// "{"name":"T'Pol","species":"Vulcan","station":"Science Officer"}"

To do the opposite, that is create a JavaScript object from JSON text, use JSON.parse():

let crewJson = '{"name": "Travis", "species": "Human", "station": "Helm"}';
*let crew = JSON.parse(crewJson);
console.log(crew);
// Object {name: "Travis", species: "Human", station: "Helm"}

Resources

You can’t perform that action at this time.