🍃 Leaf

Eloquent JavaScript

Written by Marijn Haverbeke.

Values, Types and Operators

Numbers

Strings

Operators

Short-circuiting:

Program Structure

Make code readable:

The brake jumps out of the loop. The continue jumps to the loop’s next iteration.

Functions

Closure

Is the ability to access a local binding created in a context that is no longer active (a function call that has ended). This feature (being able to reference a specific instance of a local binding in an enclosing scope) is called closure.

// E.g.:
function bindTo(n) {
  let localBinding = n;
  return () => localBinding;
}

let one = bindTo(1);
let hi = bindTo('hi'); 

console.log(one()); // --> 1
console.log(hi()); // --> hi
// E.g.:
function multiplier(factor) {
  return number => number * factor;
}

let double = multiplier(2);
let half = multiplier(0.5);

console.log(double(2)); // --> 4
console.log(half(2)); // --> 1

Functions Visibility

Functions Scope

Data Structures: Objects and Arrays

Objects Properties

Some Array Methods

Mutability

// E.g.:
let a = { value: 1 };
let b = a;
let c = { value: 1 };

console.log(a === b); // --> true
console.log(a === c); // --> false

a.value = 3;
console.log(b.value); // --> 3

Some String Methods

Note: JavaScript stops or warns you when binding with a name already taken for let and const declarations, not for var nor function.

Higher-Order Functions

A function that operates on other functions, taking them as arguments or returning them, are called higher-order functions. Calls can be stacked in a single expression. When returning functions they can be called immediately.

// E.g.:
function hof(args) {
  return (moreArgs) => {
    return (moreAndMoreArgs) => {
      console.log(args, moreArgs, moreAndMoreArgs);
    }
  }
}

hof('Please')('don\'t')('do it');
// --> Please don't do it

Strings and Character Codes

// E.g.:
let horse = '🐴';

console.log(horse.length); // --> 2
console.log(horse[0]); // --> ďż˝ (invalid character, half-character)
console.log(horse.charCodeAt(0)); // --> 55357 (half-character code)
console.log(horse.codePointAt(0)); // --> 128052 (actual character code)

The Secret Life of Objects

Symbols

// E.g.:
let name = Symbol('description');
let animal = {
  name: 'Timon',
  [name]: 'Pumba'
};

console.log(animal); // --> {name: 'Timon', Symbol(description): 'Pumba'}
console.log(animal.name); // --> Timon
console.log(animal[name]); // --> Pumba

Functions can also work with symbols:

// E.g.:
let symbol = Symbol();
let object = {
  [symbol]() {
    return 'Random text';
  }
};

console.log(object[symbol]());
// --> Random text

Symbols are always unique, even when defined with the same args.

// E.g.:
let name = Symbol('description');
let nameAgain = Symbol('description');

console.log(name == nameAgain);
// --> false

When declaring through new Object syntax (created by class or function notation), symbols can be applied directly to Object.prototype.

// E.g.:
class Car {
  constructor(owner) {
    this.owner = owner;
  }
}

let acmeCar = new Car('Coyote');

let owner = Symbol();
Car.prototype[owner] = 'Road Runner';

console.log(acmeCar.owner);
// --> Coyote
console.log(acmeCar[owner]);
// --> Road Runner

Getters, Setters and Statics

// E.g.:
class Metric {
  constructor(value, magnitude, unit) {
    this.value = value;
    this.magnitude = magnitude;
    this.unit = unit;
  }
  set resetUnit(value) {
    console.log('Resetting unit.');
    this.unit = value
  }
  get status() {
    return `The ${this.magnitude} is ${this.value}${this.unit}.`;
  }
}

let timer = new Metric(24, 'time', 's');

timer.resetUnit = 'hs';
// --> Resetting unit.

console.log(timer.status);
// --> The time is 24hs.

Inheritance

The instanceof operator sees through inherited types.

Bugs and Errors

Regular Expressions

Definition

// E.g.:
let re = new RegExp('abc');

// Or:
let re = /abc/;

Test Method

// E.g.:
console.log(re.test('aac')); // --> false
console.log(/abc/.test('aac')); // --> false

Backslash Codes (character groups)

Set of Characters

// E.g.: Equivalent expressions.
console.log(/[0123456789]/.test('5')); // --> true
console.log(/[0-9]/.test('5')); // --> true

Backslash codes still work when used between square brackets (in a set of characters). But other symbols, such as + or ., lose their special meanings.

Special Symbols

Using [^] can refer to any character, including a new line.

Counters

Braces after an element indicates that it should occur a precise number of times (or a range of times).

// E.g.:
console.log(/a{4}/.test('aaaa')); // --> true
console.log(/\d{1, 3}/.test('36')); // --> true

// Omitting the last value means an open-ended range (any amount of times).
console.log(/a{1,}/.test('aaa')); // --> true

Grouping Subexpressions

It’s possible to group subexpressions with parentheses:

// E.g.:
console.log(/(he)+/.test('hehehe'));
// --> true

The whole match is always the first element. A group matched multiple times, will return only the last match.

// E.g.:
console.log(/(\d)+/.exec('123'));
// --> ['123', '3']

Execute

The exec method returns null if no match was found, and returns an object with information about the match otherwise.

// E.g.:
let match = /\d+/.exec('Number is 12');
console.log(match);
// --> ['12']
console.log(match.index);
// --> 10

String Match

Strings have a match method that behaves similarly.

// E.g.:
console.log('Number is 12'.match(/\d+/));
// --> ['12']

Choice Patterns

Grouping several patterns within parentheses and separating them with a pipe (|) makes each one of them an option (it will match just one of them at a time).

// E.g.:
console.log(/\b(apple|orange|banana)\b/.test('apple'));
console.log(/\b(A|B|C)\b/.test('AB'));
// --> true
// --> false

Replace Method

// E.g.:
let string = "dogs and cats";
console.log(string.replace(/(\w+) and (\w+)/, "$2 and $1"));
// --> cats and dogs

Example:

let string = "DON'T SCREAM";
console.log(string.replace(/\b(\w+)\b/g, str => str.toLowerCase()));

Greedy Matching

// E.g.:
let commentedCode = "x = 1 /* comment */+/* comment */ 10";

// Try to replace a comment block, and it's content.
console.log(commentedCode.replace(/\/\*[^]*\*\//g, ""));
// --> x = 1  10
// E.g.: Try to replace a comment block, and it's content the right way.
console.log(commentedCode.replace(/\/\*[^]*?\*\//g, ""));
// --> x = 1 + 10

Escape Characters

// E.g.: Set of characters with special meanings.
let escapeRegex = /[\\?$^()[]{}.+*]/g;
let text = 'To be escaped: \\ ? $ ^ ( ) [ ] { } . + *';
console.log(text.replace(escapeRegex, '\\$&'));
// --> To be escaped: \\ \? \$ \^ ( ) [ ] \{ \} \. \+ \*

LastIndex Property

// E.g.:
let re = /\d/g;
console.log(re.exec('the number is 180'));
// --> ['1']
console.log(re.lastIndex)
// --> 15
// '1' is at 14

// Matching will start at index 15.
console.log(re.exec('now it is 90'));
// --> null

Options:

Note: on sticky (y), match will succeed only if it starts directly with lastIndex, whereas global will search ahead.

Modules

Asynchronous Programming

Promises

// E.g.:
let promise = new Promise((resolve, reject) => { /* ... */ });
let value = promise
  .then(value => { /* use value */ })
  .catch(reason => { /* use reason */ });

// Or:
let promiseValue = new Promise((resolve, reject) => { /* Do something. */ }))
  .then(value => { /* use value */ })
  .catch(reason => { /* use reason */ });

Async Functions

// E.g.:
async function anotherPromise() {
  return 1;
}

async function waitAndDo() {
  // Code "stops" here at `await` until `anotherPromise` finishes.
  return await anotherPromise();
}

let any = undefined;
waitAndDo()
  .then(value => {
    any = value;
    console.log(any);
  });
console.log(any);
// --> undefined (last log)
// --> 1 (log from `then`)

Generators

Generators produce an iterable object.

// E.g.:
function* powers(n) {
  for (let current = n;; current *=n) {
    yield current;
  }
}

for (let power of powers(2)) {
  if (power > 30) break;
  console.log(power);
}
// --> 2
// --> 4
// --> 8
// --> 16

JavaScript and the Browser

The Web

HTML

The DOM

Trees

Examples:

There are 12 codes (2, 4, 5, 6 and 12 are deprecated).

Moving Through the Tree

Every node has a parentNode and only element nodes have childNode. Also firstChild and lastChild, similarly, previousSiblind and nextSilbling (when there isn’t a previous or a next, it returns null). There is also the children property, but contains only element nodes (nodetype = 1), not as childNodes which contains all of them.

Changing the Document

A node can exist only in one place. That means if we take an element from a certain part of the DOM tree and we insert it into a different part, it will first be removed from the dom, and then inserted in its new place. The replaceChildren method takes the new node, and the one to be replaced as arguments.

Creating Nodes

Attributes

Layout

Useful element properties:

Query Selectors

Unlike getElementsByTagName, the object returned by querySelectorAll is not live.

Handling Events

Default Actions

To prevent the default action to be fired when an event occurs, use event.preventDefault() method. Some events can not be intercepted.

Pointer Events

Mouse Clicks

When a click is done, mousedown, mouseup and then click fires.

Some data from mouse events:

Web Worker

A different thread can be fired with a web worker. Workers do not share their global scope, they need to use postMessage and onMessage to communicate with other scopes.

// worker.js
self.onMessage(e => {
  console.log('Worker received:', e.data);
  self.postMessage('Copy!']);
});
// main.js
let worker = new Worker('worker.js');

worker.onMessage(e => {
  console.log('Main received:', e.data);
});

worker.postMessage('Hi worker!');
// --> Worker received: Hi worker! (from worker.js)
// --> Main received: Copy! (from main.js)

Each side receives a copy of the object sent, rather than the value itself.

Timers

Debouncing

A common practice to avoid calling a function, or handling an event too often, is using a setTimeout, this is called debouncing the event.

// E.g.:
let scheduled = false;
window.addEventListener('click', event => {
  if (!scheduled) {
    setTimeout(() => {
      // Do something...
      // When it's done, let it schedule
      // again by resetting the value.
      scheduled = false;
    }, 250);
  }
  schedule = event;
});

HTTP and Forms

The Protocol

Browsers and HTTP

Forms are the standard tool to handle a group of inputs as a whole with JavaScript.

<!-- E.g.: -->
<form method="GET" action="example/message.html">
  <p>Name: <input type="text" name="name"/></p>
  <p>Last name: <input type="text" name="lname"/></p>
  <p><button type="submit">Send</button></p>
</form>

Fetch

// E.g.:
fetch('example/data.txt')
  .then(res => console.log(res.status));
// --> 200

Use text method to get the content of a response.

// E.g.:
fetch('example/data.txt')
  .then(res => res.text())
  .then(text => console.log(text));
// --> Content of data.txt

Similarly, use json to parse from plain text JSON file to JavaScript object notation.

// E.g.:
fetch('example/data.txt')
  .then(res => res.json());

Focus

To move the focus to an element use focus. To remove focus use the blur method. If the user is expected to interact immediately use autofocus. We can influence the order of elements when moving through with TAB using the tabindex attribute. A tabindex of -1 makes tabbing skip over an element.

The Form as a Whole


Reference: Eloquent JavaScript (eloquentjavascript.net).