JavaScript Reference

Kip Landergren

(Updated: )

My cheat sheet for JavaScript covering personal conventions, prototypical inheritance, and code snippets.

Contents

Personal Conventions

Writing JavaScript Code

Filenaming

OK:

tree.js
balanced-tree.js
balancedTree.js
structures.tree.js

Not OK:

foo_bar.js
foo bar.js

References:

Prototypical Inheritance

prototypical inheritance in JavaScript

ECMAScript Versions

Note: “ECMA-262” refers to the ECMAScript Language Specification.

December 2009 5th Edition (ES5) strict mode
June 2015 ECMAScript 2015 (ES2015) ECMAScript 6th Edition (ES6) class declarations, modules, iterators, Python-style generators, arrow functions, let and const, and others
June 2016 ECMAScript 2016 (ES2016) ECMAScript 7th Edition (ES7) ...
June 2017 ECMAScript 2017 (ES2017) ECMAScript 8th Edition (ES8) ...
June 2018 ECMAScript 2018 (ES2018) ECMAScript 9th Edition (ES9) ...
June 2019 ECMAScript 2019 (ES2019) ECMAScript 10th Edition (ES10) ...
June 2020 ECMAScript 2020 (ES2020) ECMAScript 11th Edition (ES11) ...

JavaScript Terminology

binding / bound
the association of a specific object to this
executor
the function object wrapped by the Promise
falsy
the quality of an object that evaluates to false
fulfilled
the state of a Promise when a result value is available
hoisting
term used to provide a conceptual understanding of how variable and function declarations are put into memory during the compile phase; they can be thought of as being “hoisted” to the top of the file
key
an enumerable property of an object
own property
a property belonging to an object and not a member of its prototype chain
pending
the state of a Promise when not “fulfilled” or “rejected”
rejected
the state of a Promise when a result value is not available but a reason (typically an Error) for rejection is available
resolved
a Promise that is either “settled” or has been “locked in” to match the state of another Promise
settled
a Promise that is either “fulfilled” or “rejected”
truthy
the quality of an object that evaluates to true

Resources

FAQ

Why is JavaScript called a “multi-paradigm” language?

A paradigm refers to the qualities of the programming language, and JavaScript code can be written in both functional and procedural styles.

What exactly is console?

Refers to the JavaScript runtime’s debugging console object; is not standardized across browsers / implementations. MDN Console reference.

Important: pay attention to whether the logged JavaScript object was evaluated at log time vs “just now” (indicated via blue ⓘ).

javascript-study

README.md

# JavaScript Study

Personal study area for ECMAScript versions

## quickstart

```
$ find . -type f -name '*.js' | xargs -t -I {} node {}
```

console.js

/* levels */
console.log("log");
console.debug("debug");
console.info("info");
console.warn("warn");
console.error("error");

es6/built-in-objects/JSON.js

/* convert object to JSON */
const obj = {};
obj.foo = "bar";

console.log(JSON.stringify(obj));

/* convert ES6 Map to JSON */
const m = new Map();
m.set("a", "alfa");
m.set("b", "bravo");
m.set("c", "charlie");

const entries = Object.fromEntries(m);

/* `stringify(value, replacer, space)` converts `value` to a JSON string */
console.log(JSON.stringify(entries));
console.log(JSON.stringify(entries, null, "  ")); /* pretty-print */

/* deep copy array of objects; deep copy array of arrays */
const deepCopy = JSON.parse(
  JSON.stringify([
    [1, 2],
    [3, 4],
  ])
);

es6/built-in-objects/array.js

/* arrays */

/* array constructors (all equivalent) */
const arr0 = Array(1, 2, 3);
const arr1 = new Array(1, 2, 3);

/* array literal */
const arr2 = [1, 2, 3];

/* array with an initial length */
const initialLength = 10;
const arr3 = Array(initialLength); /* array with `initialLength` empty slots */

const arr = ["a", "b", "c", "d"];

/* properties */

/* array length; size of array; array size */
arr.length; /* 4 */

/* methods */

/* array concat; does not modify either array */
const concattedArray = arr.concat(["e"]);

/* `flat(depth = 1)`; flatten array recursively to depth */
["a", "b", ["c", "d"]].flat(); /* [ 'a', 'b', 'c', 'd' ] */

/* `join()` to create string from array */
arr.join(""); /* abcd */
arr.join(","); /* a,b,c,d */

/* iterate elements, indices, and the array */
arr.forEach((element, idx, arr1) => {
  /* do something */
});

/* map over elements, returning a new array */
arr.map((element, idx, arr1) => {
  /* do something */
});

/* `filter` returns elements that pass the test */
arr.filter((element, idx, arr1) => {
  /* condition */
});

/* `find` returns the first _element_ matching condition or `undefined` */
arr.find((element, idx, arr1) => {
  /* find condition */
});

/* `findIndex` returns the first _index_ satisfying the test condition, or
-1. Best used when searching an array of objects. If searching within an array
of primitives, consider using `indexOf` for clarity */
arr.findIndex((element, index, array) => {
  /* test condition */
});

/* `indexOf(element, startIndex = 0)` returns first index of found element, or
-1. This is especially useful for array of primitive types. For arrays of
objects, consider using `findIndex` */
arr.indexOf("c"); /* 2 */

/* array reduce */
let initialValue = new Map();
arr.reduce((memo, v, i, arr1) => {
  /* create next memo */
}, initialValue);

/* `slice` returns a shallow copy of the original. for numbers and strings this
is not a problem, but if the array you are slicing contains objects, those
object references will be copied. does not modify the original array */
const arr7 = arr.slice(0, 2); /* [ 'a', 'b' ] */

/* array limit / array slice */
const only5 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].slice(0, 5); /* [0,1,2,3,4] */

/* examples */
const numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

/* array filter example */
const even = numbers.filter((element, idx, arr1) => element % 2 === 0);

/* array find example */
const firstGreaterThan3 = numbers.find((n) => 3 < n); /* 4 */
const firstGreaterThan20 = numbers.find((n) => 20 < n); /* undefined */

/* array reduce example */
const sum1 = numbers.reduce((m, v) => m + v);
const sum2 = numbers.reduce((m, v) => m + v, numbers[0]); /* equivalent */

/* convert array to map */
/* create a histogram from an array using reduce */
const histogram = ["a", "a", "a", "b", "b"].reduce((m, v) => {
  let count = m.get(v) ?? 0;
  count++;
  return m.set(v, count);
}, new Map()); /* Map(2) { 'a' => 3, 'b' => 2 } */

/* array to set */
const s = new Set(arr);

/* truncate array to n elements / array take / limit array using `slice()` */
const n = 2;
arr.slice(0, n + 1);

/* remove multiple items from an array */
const filtered = arr.filter((element, index, arr) => {
  /* some condition */
});

/* create an array range (note: it may be more readable to use a for loop) */
const threeElementArr = [...Array(3).keys()]; /* [ 0, 1, 2 ] */

/* check if array has duplicates / array unique */
const noDupes = new Set(arr);
const hasDupes = noDupes.size !== arr.length;

/* take the average of an array */
const avg = numbers.reduce((memo, n) => memo + n) / numbers.length;

/* get random element from array */
const randomElementOfArr = (arr) => arr[Math.floor(Math.random() * arr.length)];

/* array includes (array contains, array has) */
arr.includes("b"); /* true */

es6/built-in-objects/array/fill.js

/* array fill. create an array. generate an array of nums */
const arr = Array(10).fill(-1);

/* can also pass start and end indices. arr will be filled up to but not
including the end index */
arr.fill(0, 5, 10);

/* can fill with any static object */
const nArr = Array(10).fill(null);

es6/built-in-objects/array/from.js

/* array from usage / Array.from usage */

/* with optional `mapFn` applied to each element */
const arr1 = Array.from([1, 2, 3], (element, idx) => {
  /* do work */
  return element;
});

/* array from array */
const arr2 = Array.from([1, 2, 3]);

/* array from string */
const arr3 = Array.from("hello"); /* ['h', 'e', 'l', 'l', 'o' ] */

/* array from set, also: how to remove duplicates from array */
const arr4 = Array.from(new Set(arr3)); /* ['h', 'e', 'l', 'o' ] */

/* array from map */
const nato = new Map();
nato.set("a", "alfa");
nato.set("b", "bravo");

const arr5 = Array.from(nato); /* [ [ 'a', 'alfa' ], [ 'b', 'bravo' ] ] */

/* array from array-like object */
const obj = { length: 3 };
const arr6 = Array.from(obj, (_, idx) => idx); /*  */
console.log(arr6); /* [ 0, 1, 2 ] */

es6/built-in-objects/array/push.js

/* array push */
const arr = [];

/* [] <--push-- 1 */
/* [ 1 ] */

/* array append element */
arr.push(1); /* [ 1 ] */
arr.push(2); /* [ 1, 2 ] */
arr.push(3); /* [ 1, 2, 3 ] */

/* array append elements */
arr.push(4, 5); /* [ 1, 2, 3, 4, 5 ] */

/* push returns the new array length */
const newLength = arr.push(6); /* 6 */

es6/built-in-objects/array/reverse.js

/* Array.prototype.reverse */

/* array reverse works in-place */
const arr = [1, 2, 3];

arr.reverse();

console.log(arr[0]); /* 3 */

es6/built-in-objects/array/shift.js

/* array shift */

const arr = [1, 2, 3];

/* shift returns the first element and modifies the array */
let firstElement = arr.shift(); /* firstElement=1, arr=[2,3] */
firstElement = arr.shift(); /* firstElement=2, arr=[3] */
firstElement = arr.shift(); /* firstElement=3, arr=[] */

firstElement = arr.shift(); /* firstElement=undefined, arr=[] */

es6/built-in-objects/array/slice.js

/* array slice */

const arr = ["apple", "banana", "cantaloupe", "durian"];

/* returns an _array_ that is a shallow copy of the portion of the original
array. original array not modified */

/* start is index to included, end is last index _not_ included */
const abArr = arr.slice(0, 2);
const bcArr = arr.slice(1, 3);
const bcdArr = arr.slice(1);

/* last n elements of an array */
const cdArr = arr.slice(-2);

/* last element of an array; array last */
const dArr = arr.slice(-1);

es6/built-in-objects/array/sort.js

const arrNums = [8, 6, 7, 5, 3, 0, 9, 42];

/* sort happens in place  */

/* gotcha: without compareFunc supplied values are converted to strings and
then sorted */
arrNums.sort(); /* [ 0, 3, 42, 5, 6, 7,  8, 9 ] */

/* to sort numbers, pass a compareFunc. note that this compareFunc is not
asking "is a greater than b?" per se, it is asking "is a positioned greater
than b in the desired sort order?" */

/* sort ascending */
arrNums.sort((a, b) => {
  /* return 0 if a and b are equal */
  /* return -1 (or any value < 0) if a is less in the sort order than b */
  /* return 1 (or any value > 0) if a is greater in the sort order than b */
  return a - b;
});

/* sort descending */
arrNums.sort((a, b) => {
  return b - a;
});

/* sort an array of strings; sort strings */
const arrStrings = ["apple", "banana", "cantaloupe"];
arrStrings.sort((a, b) => a.localeCompare(b));

es6/built-in-objects/array/splice.js

/* array splice. array remove items / array replace items  */

/* notes:  */
/*   - modifies the underlying array */
/*   - modifies in place */
/*   - returns deleted items, or empty array */

/* at index 0, remove 1 item, replacing it with nothing  */
const fruit = ["apple", "banana", "currant"];
fruit.splice(0, 1); /* fruit is now ["banana", "currant"] */

/* starting at index 2, delete 0 items, and add items 'd' and 'e' */
const letters = ["a", "b", "c"];
letters.splice(2, 0, "d", "e"); /* letters is now ["a", "b", "c", "d", "e"] */

es6/built-in-objects/array/unshift.js

/* array unshift */

const arr = [];

/* --unshift(3)--> [] */
/* [ 3 ] */

/* array prepend element */
arr.unshift(3); /* [ 3 ] */
arr.unshift(2); /* [ 2, 3 ] */
arr.unshift(1); /* [ 1, 2, 3 ] */

/* array prepend elements */
arr.unshift(-1, 0); /* [ -1, 0, 1, 2, 3] */

/* unshift returns the new array length */
const newLength = arr.unshift(-2);

es6/built-in-objects/date.js

/* IMPORTANT NOTE: parsing dates and formatting dates appears to be (is?) a
massive pain in JavaScript; look into `Intl.DateTimeFormat` or libraries
moment.js and date.js (and their disclaimers) and determine if you really need
a separate library */

/* number of milliseconds elapsed since epoch */
const now = Date.now();

/* get the current date and time, right now */
const today = new Date();

/* format date like `2022 Wed Feb 23 15:49:38` */
const formatDate = (date) => {
  let year = date.getFullYear();

  let shortDay;
  switch (date.getDay()) {
    case 0:
      shortDay = "Sun";
      break;
    case 1:
      shortDay = "Mon";
      break;
    case 2:
      shortDay = "Tue";
      break;
    case 3:
      shortDay = "Wed";
      break;
    case 4:
      shortDay = "Thu";
      break;
    case 5:
      shortDay = "Fri";
      break;
    case 6:
      shortDay = "Sat";
      break;
    default:
      /* no default */
      break;
  }

  let shortMonth;
  switch (date.getMonth()) {
    case 0:
      shortMonth = "Jan";
      break;
    case 1:
      shortMonth = "Feb";
      break;
    case 2:
      shortMonth = "Mar";
      break;
    case 3:
      shortMonth = "Apr";
      break;
    case 4:
      shortMonth = "May";
      break;
    case 5:
      shortMonth = "Jun";
      break;
    case 6:
      shortMonth = "Jul";
      break;
    case 7:
      shortMonth = "Aug";
      break;
    case 8:
      shortMonth = "Sep";
      break;
    case 9:
      shortMonth = "Oct";
      break;
    case 10:
      shortMonth = "Nov";
      break;
    case 11:
      shortMonth = "Dec";
      break;
    default:
      /* no default */
      break;
  }

  let dayOfMonth = `${date.getDate()}`.padStart(2, "0");
  let hours = `${date.getHours()}`.padStart(2, "0");
  let minutes = `${date.getMinutes()}`.padStart(2, "0");
  let seconds = `${date.getSeconds()}`.padStart(2, "0");

  return `${year} ${shortDay} ${shortMonth} ${dayOfMonth} ${hours}:${minutes}:${seconds}`;
};

es6/built-in-objects/function.js

/* bind usage */
const obj = { foo: "foo" };

/* IMPORTANT NOTE: cannot use arrow syntax here due to no bindings to `this` or
super` */
const unboundGetFoo = function () {
  return this.foo;
};
console.log(unboundGetFoo()); /* `undefined` */

/* `bind` attaches the called-on function to the scope of the first
argument. This is useful for retaining scope when passing functions. */
const boundGetFoo = unboundGetFoo.bind(obj);
console.log(boundGetFoo()); /* foo */

/* generator functions */
/* stride over array; see https://stackoverflow.com/questions/44710083/javascript-for-loop-with-certain-step */
function* stride(arr, n) {
  for (let i = 0; i < arr.length; i += n) {
    yield arr.slice(i, i + n);
  }
}

/* note: there is not a way to declare a generator function like `const f: function* = () => {}*/

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];

/* array stride by 2 */
for (let s of stride(arr, 2)) {
  console.log(s);
}

es6/built-in-objects/map.js

/* maps */

/* map constructors */
const m = new Map();
const m2 = new Map([
  ["red", "#FF0000"],
  ["green", "#00FF00"],
  ["blue", "#0000FF"],
]);

/* no constructor for map with default value */

/* properties */

/* map size; map length */
m.size; /* 0 */

/* instance methods */

/* map set */
m.set("a", "alfa");
m.set("b", "bravo");
m.set("c", "charlie");

/* map get */
m.get("c"); /* charlie */
m.get("d"); /* undefined */

/* map delete; map remove */
m.delete("c"); /* true */
m.delete("d"); /* false */

/* map has; map contains; map check if contains */
m.has("d"); /* false */

/* map clear; map empty */
m.clear();

/* reset data for next section */
m.set("a", "alfa");
m.set("b", "bravo");
m.set("c", "charlie");

/* `entries()` is an Iterator, not an array; map entries */
m.entries();

/* `keys()` is an Iterator, not an array; map keys */
m.keys();

/* `values()` is an Iterator, not an array; map values */
m.values();

/* usage notes */

/* map to array (insertion order is preserved) */
Array.from(m); /* [['a', 'alfa'], ['b', 'bravo'], ['c', 'charlie']] */

/* map iterate keys; iterate map keys */
for (const k of m.keys()) {
  /* work with k */
}

/* map iterate values; iterate map values */
for (const v of m.values()) {
  /* work with v */
}

/* map iterate entries, version 1; iterate map entries */
for (const e of m.entries()) {
  /* .entries() returns an array of [k_n, v_n] for every key, so e is just [k, v] */
  const k = e[0];
  const v = e[1];
}

/* map iterate entries, version 2, using destructuring assignment */
for (const [k, v] of m.entries()) {
  /* work with k and v */
}

/* map iterate values, keys, and the map */
m.forEach((v, k, m1) => {
  /* do something */
});

/* get random element from map; map random element */
const randomElementOfArr = (arr) => arr[Math.floor(Math.random() * arr.length)];

const randomEntryOfMap = (m) => {
  return randomElementOfArr([...m.entries()]);
};

console.log(randomEntryOfMap(m));

/* check if map is empty; map check empty */
m.size === 0;

es6/built-in-objects/math.js

/* math */

/* integer division */
const quotient = Math.floor(22 / 7);
const remainder = 22 % 7;

/* random integer, from 0 to `max` - 1 (exclusive of `max`) */
const randomInt = (max) => {
  return Math.floor(Math.random() * max);
};

randomInt(2); /* random 0 or 1 */
randomInt(3); /* random 0, 1, or 2 */

es6/built-in-objects/number.js

/* number */

/* convert string to number; coerce string to number */
const ten = Number("10"); /* use constructor */

/* parseInt */
const alsoTen = parseInt("10", 10); /* supply radix of 10 */
const stillTen = parseInt("10"); /* default radix is 10 */
const notTen = parseInt("10", 2); /* supply radix of 2 */
const huh = parseInt("b"); /* unparsable; ends up NaN  */

/* isNaN */
/* do not use: */
if (huh === NaN) {
  /* will never succeed: NaN always compares unequal to any other value, including NaN */
}

/* use: */
if (isNaN(huh)) {
  /* will succeed: using Number.isNaN is the preferred way to check */
}

/* `toFixed` returns a string formatting the number in fixed-point notation  */
const num = 123.456;
num.toFixed(2); /* "123.46" */

/* max safe integer */
Number.MAX_SAFE_INTEGER; /* 2^53 – 1 */

es6/built-in-objects/object.js

/* object */

/* `entries()` iterates string-keyed properties of an object */
const red = { name: "red", hex: "#FF0000", utility: 3 };
for (const [k, v] of Object.entries(red)) {
  console.log(k, v);
}

/* understanding methods vs functions */

/* I believe the reason MDN makes the distinguishment that JavaScript does not
have traditional methods compared to other programming languages is that you
can add functions as properties to any object, and those functions are not
accessible from other instances. Accessible functions may be created by
creating them on the function prototype, which is more in line with a
class-based programming language's methods */

/* take for example 3 arrays: */
const a = Array.from([1, 2]);
const b = Array.from([3, 4]);
const c = [5, 6];

/* we can create a custom function on an _object_ */
a.myCustomFunction = function () {
  console.log("my zeroth index contains:", this[0]);
};

a.myCustomFunction();

/* and this is inaccessible to other instances created from the Array function
or the array literal: */

/* b.myCustomFunction(); */ /* error */
/* c.myCustomFunction(); */ /* error */

/* but if we define the function on Array.prototype, that becomes accessible to
all objects with that prototype */
Array.prototype.protoCustomFunction = function () {
  console.log("my FIRST index contains:", this[1]);
};

a.protoCustomFunction();
b.protoCustomFunction();
c.protoCustomFunction();

/* hasOwn / hasOwnProperty  */

/* checks whether the property belongs to an object and not to a member of its
prototype chain */

/* create a function F, called a constructor, that sets property `a` of the
returned object to 2. remember that this is _not_ an instance of F. */
const F = function () {
  this.a = 2;
};

/* extend the object `F.prototype` with a property `b` (empty function just for
demonstration) */
F.prototype.b = function () {};

/* create an instance of F */
const f = new F();

/* set a new property `c` on the object `f` */
f.c = "charlie";

/* test the properties of f */

/* true, because the constructor F creates property `a` */
Object.hasOwn(f, "a");

/* false, because `b` is not created in the constructor, or on `f` directly */
Object.hasOwn(f, "b");

/* true, because `c` is created on `f` directly */
Object.hasOwn(f, "c");

/* copy an object */
const source = { alpha: "a" };
const copy = Object.assign({}, source);

es6/built-in-objects/set.js

/* spec: https://262.ecma-international.org/7.0/#sec-set-objects */

/* Empty Set */
const empty = new Set();

/* Set from array */
const s = new Set(["a", "b", "c"]);

/* set properties */
s.size;

/* do not use `length`, would return `undefined`: */
/* s.length; */

/* set methods */

/* add(); insertion order is preserved */
s.add("d"); /* returns the set with added value */
s.add("e").add("f"); /* chaining is OK */

/* delete(); no chaining */
s.delete("d"); /* returns boolean */

/* has() */
s.has("z"); /* returns boolean */

/* clear(); removes all elements */
s.clear(); /* returns `undefined` */

/* iterating sets */

/* returns Iterators, not Sets, not Arrays */
s.values(); /* returns Iterator, with objects in _insertion order_ */
s.keys(); /* same as `values()` */
s.entries(); /* returns Iterator, with [value, value] elements */

s.forEach((value, key, s1) => {
  /* do work */
});

for (const v of s) {
  /* work with v */
}

for (const v of s.values()) {
  /* work with v */
}

for (const k of s.keys()) {
  /* work with k (`keys()` same as `values()`)  */
}

for (const [k, v] of s.entries()) {
  /* work with k and v, which are the same */
}

/* usage notes */

/* add all items from an array to a set (would remove any duplicates) */
["a", "b", "c"].forEach((v) => s.add(v));

/* convert set into an array; set to array */
const arr = [...s];

/* add all items from another set to a set */
const setA = new Set(["1", "2"]);
const setB = new Set();
setA.forEach((value, key, s1) => setB.add(value));

/* combine two sets */
const setC = new Set([...setA, ...setB]);

/* remove duplicates from an array */
const dupes = Array.from("wwwwwwwwwwwwiiwwiwwiiinnnwnnn");
const noDupes = new Set(dupes); /* Set(3) { 'w', 'i', 'n' } */
const noDupesArr = [...noDupes]; /* [ 'w', 'i', 'n' ] */

/* get random element from Set */
const randomElementOfArr = (arr) => arr[Math.floor(Math.random() * arr.length)];

const randomElementOfSet = (s) => {
  return randomElementOfArr([...s]);
};

randomElementOfSet(s);

es6/built-in-objects/string.js

/* https://262.ecma-international.org/7.0/#sec-string-objects */

/* a JavaScript String is _immutable_ */

/* string literals */
const hello = "hello";
const space = ` `;
const world = "world";

/* multiline string (multi-line strings; template literals) */
const multi = `hello
world`;

/* string interpolation via template literal */
const greeting = `I said: "${hello} ${world}"`;

/* split string into array */

/* preferred (except when dealing with emojis): */
const arr1 = Array.from(hello);
const arr2 = [...hello];

/* avoid, due to potential for `hello` to contain wide characters (code points
higher than 65536 and needing pseudo characters for representation) */
const arr3 = hello.split("");

/* for strings containing emojis that are themselves created from multiple
characters, consider using a specialized library like
https://github.com/orling/grapheme-splitter */

/* replace character at index in string */
const arr4 = Array.from(hello);
arr4.splice(0, 1, "H"); /* at index 0, delete 1 element, replace with 'H' */
const improvedHello = arr4.join(""); /* Hello */

es6/built-in-objects/string/charCodeAt.js

/* string charCodeAt (string char code at, string character code at) */

/* returns an integer between 0 and 65535 representing the UTF-16 code unit at
the given index */
"abc".charCodeAt(0); /* 97 */

es6/built-in-objects/string/concat.js

/* string concatenation / string append */
const hello = "hello";
const world = "world";

const cat1 = hello + " " + world; /* hello world */

/* original string is unmodified (strings in JavaScript are immutable) */
const cat2 = hello.concat(" ", world); /* hello world */

es6/built-in-objects/string/includes.js

/* string includes (string contains)  */

"hello".includes("ll"); /* true */
"hello".includes("world"); /* false */

/* optional index from which to start the search */
"hello".includes("h", 1); /* false */

/* notes: case sensitive */

es6/built-in-objects/string/padStart.js

/* left pad */
const date = new Date();
const dayOfMonth = `${date.getDate()}`.padStart(2, "0"); /* 01, 02, etc */

es6/built-in-objects/string/replace.js

/* string replacement (string replace) */
/* two forms: regexp and substring. review docs for expanded usage using
replacer functions */

/* regexp replacement */
const word = "23a4?e!r0os".replace(
  /[^a-zA-Z]+/g,
  ""
); /* 'aeros'. removes all but a-z and A-Z */

/* substring replacement */
const greeting = "hello world".replace("world", "earth");

es6/built-in-objects/string/slice.js

/* string slice */

/* slice(indexStart, [indexEnd]) where indexEnd is index of first character to _exclude_ */

/* note: original string not modified (strings in javascript are immutable) */
"hello world".slice(6); /* "world" */
"hello world".slice(0, 5); /* "hello" */

es6/built-in-objects/string/startsWith.js

/* string starts with */
const greeting = "hello world";

console.log(greeting.startsWith("hello")); /* true */

es6/expressions-and-operators/destructuring-assignment.js

/* destructuring assignment */

/* _values_ from an array: */
const [a, b] = ["alfa", "bravo"];
const [c, d, , f] = ["charlie", "delta", "echo", "foxtrot"]; /* ignore 'echo' */
console.log(f); /* foxtrot */
const [g, h, ...others] = ["golf", "hotel", "india", "juliett", "kilo"];
console.log(others); /* [ 'india', 'juliett', 'kilo' ] */

/* swap variables */
let [l, m] = ["lima", "mike"];
[l, m] = [m, l];
console.log(l); /* mike */

/* _properties_ from an object (order does not matter): */
const { bar, foo } = { foo: "foo", bar: "bar" };
console.log(bar); /* bar */

/* also works with maps: */
const lookup = new Map();
lookup["someKey"] = "value!";
const { someKey } = lookup;
console.log(someKey); /* value! */

es6/expressions-and-operators/evaluation.js

/* evaluation is done left to right: */

let idx = 0; /* normal assignment */
console.log(idx++); /* 0 */
console.log(++idx); /* 2 */

es6/expressions-and-operators/nullish-coalescing-operator.js

/* nullish coalescing operator */
const resp = {};
const rawItems = resp.items;
const coalescedItems = resp.items ?? [];
console.log(rawItems); /* undefined */
console.log(coalescedItems); /* [] */

es6/expressions-and-operators/optional-chaining.js

/* optional chaining */

const config = {
  url: "localhost",
  memory: {
    min: "30M",
    max: "60M",
  },
};

config.cpu?.cores; /* undefined */

/* patterns */
const cores = config.cpu?.cores ?? 1; /* nullish coalescing */

es6/statements-and-declarations/do-while.js

/* do...while (do-while, do while) statements execute the block before
evaluating the condition. if the condition evaluates to `true` the block is
executed again. if the condition evaluates to false, the statement following
the do-while loop will be executed */
let i = 0;
do {
  console.log(i);
  i++;
} while (i < 10);

/* `break` can be used to short circuit */
let j = 0;
do {
  j++;
  if (10 < j) {
    break;
  }
} while (j < 1000);

es6/statements-and-declarations/for-in.js

const obj = { a: 1, b: 2, c: 3 };

/* for loop; for in; iterate over an object's _enumerable_ properties */
for (const p in obj) {
  /* work with p; can access via `obj[p]` */
}

es6/statements-and-declarations/for-of.js

const iterableObject = ["a", "b", "c"];

/* for loop; for of; iterate over the values of iterable objects  */
for (const element of iterableObject) {
  /* work with element */
}

/* for loop; for of; iterate over an array */
for (const e of [1, 2, 3]) {
  /* work with e */
}

/* for loop; for of; iterate over a set */
for (const e of new Set([1, 2, 3])) {
  /* work with e */
}

/* for loop; for of; iterate over a string */
for (const c of "iteration") {
  /* work with c, the character */
}

es6/statements-and-declarations/for.js

const j = 1000;
/* for loop; basic for loop; for loop basic; vanilla for loop; */
for (let i = 0; i < j; i++) {
  /* continue / next via: */
  if (i === 2) {
    continue;
  }

  /* break via: */
  if (10 < i) {
    break;
  }
  /* do work */
}

es6/statements-and-declarations/if-else.js

const modes = ["dev", "test", "prod", "special", "customer"];
const mode = modes[Math.floor(Math.random() * modes.length)];

if (mode === "dev") {
  /* in dev */
} else if (mode === "prod") {
  /* in prod */
} else {
  /* somewhere else */
}

es6/statements-and-declarations/switch.js

const diceNumbers = [1, 2, 3, 4, 5, 6];
const randomElementOfArr = (arr) => arr[Math.floor(Math.random() * arr.length)];

const rollDice = () => {
  return randomElementOfArr(diceNumbers);
};

switch (rollDice() /* `expr` is evaluated */) {
  case "1" /* comparison done using strict equality `===` */:
    console.log("never matches");
    break; /* prevent fallthrough  */
  case 1: /* gotcha: no `break`, so `case 2:` is evaluated regardless */
  case 2:
    console.log("got a 1 or 2");
    break;
  case 3: {
    /* braces can control scope */
    let anotherRoll = rollDice();
    console.log("got a three and made another roll:", anotherRoll);
    break;
  }
  case 4: {
    let anotherRoll = rollDice();
    console.log("got a four and made another roll:", anotherRoll);
    break;
  }
  case 5:
  case 6:
    console.log("got 5 or 6");
    break;
  default:
    console.log("got some other number");
    /* `break` not required because this is the last statement, but `default`
    can exist in any position so this is extra safety in case copy / pasted or
    modified */
    break;
}

es6/statements-and-declarations/while.js

/* while statements evaluate the expression before each loop; if it evaluates
to `true` the statement is executed, otherwise the statement after is executed
*/
let i = 0;
while (i < 5) {
  i++;
}
console.log(i);

/* `break` can be used to short circuit */
let j = 0;
while (j < 1000) {
  if (j === 10) {
    break;
  }
  j++;
}

/* any condition that evaluates to a boolean can be used, even 'truthy' values
 */
const checkIsOpen = () => Math.floor(Math.random() * 2);
while (checkIsOpen()) {
  console.log("open!");
}

es6/toolbox/queue.js

/* queue */

/* FIFO first in, first out */
const queue = [];

/* [] <--push(1)-- */
/* [ 1 ] */

queue.push(1);
queue.push(2);
queue.push(3);

/* <--shift-- [ 1, 2, 3 ] */
/* 1 */

/* shift returns the first element and modifies the array */
let first = queue.shift(); /* 1 */
first = queue.shift(); /* 2 */
first = queue.shift(); /* 3 */

es6/toolbox/stack.js

/* stack */

/* LIFO last in, first out */
const stack = [];

/* [] <--push(1)-- */
/* [ 1 ] */

stack.push(1);
stack.push(2);
stack.push(3);

/* [ 1, 2, 3 ] --pop--> */
/* 3 */

/* pop returns the last element and modifies the array */
let last = stack.pop(); /* 3 */
last = stack.pop(); /* 2 */

gotchas.js

/* {} is _not_ a Map literal, it is an Object; there is no Map literal */

/* Objects are not Maps */

/* arrow function gotchas */

/* arrow functions have no `this` */

/* curly brackets require a return:  */

/* equivalent: */
[1, 2, 3].map((e) => e);
[1, 2, 3].map((e) => e);
[1, 2, 3].map((e) => {
  return e;
});

/* not equivalent: */
[1, 2, 3].map((e) => {
  e;
}); /* [undefined, undefined, undefined]; contains no return statement */
[1, 2, 3].map((e) => ({ e })); /* returns an array of objects with field e */

/* array gotchas */

/* shallow copy gotcha / shallow copy example / shallow copies example / array
shallow copy */
const obj1 = { color: "red" };
const obj2 = { color: "green" };
const obj3 = { color: "blue" };

const colors1 = [obj1, obj2, obj3];
console.log(
  colors1
); /* [ { color: 'red' }, { color: 'green' }, { color: 'blue' } ] */

/* `slice()` performs a shallow copy */
const colors2 = colors1.slice(0, 2);
console.log(colors2); /* [ { color: 'red' }, { color: 'green' } ] */

/* `slice()` copies object references, meaning `colors2`'s references will
point to the same objects as in `colors` */
colors1[0].color = "rose";

/* which means that both `colors1` and `colors2` are affected */
console.log(
  colors1
); /* [ { color: 'rose' }, { color: 'green' }, { color: 'blue' } ] */
console.log(colors2); /* [ { color: 'rose' }, { color: 'green' } ] */

/* sort example */

/* array sort gotcha / `sort()` works by converting elements into strings and
then sorting by their UTF-16 values in ascending order */
const numbers = [1000, 4, 200, 30, 5];

numbers.sort(); /* sort happens in-place */
console.log(numbers); /* [ 1000, 200, 30, 4, 5 ] */

/* so what exactly is happening? the number `1000` gets converted to its UTF-16
character code points, which are 0031 0030 0030 0300, and is compared to 4,
which is 0034, and the algorithm stops when there is a difference between
codepoints. so `0031` gets compared to `0034`, and is found to be less, despite
having 3 more successive digits and being a larger number. */

/* an intuitive way to think about this is to imagine how the array: */
/*                                                                   */
/*       ['baz', 'a', 'charlie']                                     */
/*                                                                   */
/* should be sorted. in this case it is more clear it should be:     */
/*                                                                   */
/*       ['a', 'baz', 'charlie']                                     */
/*                                                                   */
/* despite the length of the words.                                  */

/* just keep in mind: `sort()`, by default, converts elements to strings */

/* array sort numbers; to get numerical sorting, pass a function: */
numbers.sort((a, b) => {
  return a - b; /* sort ascending */
});
console.log(numbers); /* [ 4, 5, 30, 200, 1000 ] */

/* exponentiation is via `**`, not `^`! */
console.log(2 ** 3); /* 2^3 */

prototypical-inheritance.js

/* constructor */
const C = function () {};

typeof C; /* function */
typeof C.prototype; /* object */

/* where did `C.prototype` come from? it came from the invocation of
`function()`, which I think invokes the Function constructor. and it is in this
constructor that the `.prototype` property is set. I think this because
checking `hasOwn` returns `true`: */
Object.hasOwn(C, "prototype"); /* true */

/* note: we can still use `Object.hasOwn` on `C`, despite it being a function,
because a javascript function is also an object.  */

/* `C.prototype` is the object that will be used as the "prototype" (as a term,
not as a property name) for every instance of `C`. This means on any instance
of `C`, the object pointed to by the internal property `[[Prototype]]` will be
the object at `C.prototype` */

/* `C.prototype`, being an object, has its own "prototype" too, and this points
to `Object.prototype` */
Object.getPrototypeOf(C.prototype) === Object.prototype; /* true */

/* `C.prototype` can be modified to add methods for instances of `C` */
C.prototype.greet = function () {
  console.log("hello");
};

const c = new C();
typeof c; /* object */
c.greet(); /* hello */

/* so, working upward from `c`: */
Object.getPrototypeOf(c) === C.prototype; /* true */
Object.getPrototypeOf(C.prototype) === Object.prototype; /* true */
Object.getPrototypeOf(Object.prototype) === null; /* true */

/* when a property is accessed, this entire chain of prototypes is searched */

tips.js

/* replace arr.filter().map() with single arr.reduce() to prevent multiple
passes */