JavaScript Notes
- Basics
- Numbers
- Functions
- OOP
- DOM (Document Object Model)
- Handling events
- Pitfalls
- JSON
- Asynchronicity
- References
Basics
-
Variables:
-
Global variables live as long as the page.
-
If we forget to declare a variable before using it, it’ll always be global even if we first use it in a function.
-
let
creates block-level variables. -
const
creates constants. -
var
is the traditional way of declaring variables.1 2 3 4 5 6 7
// i visible here (undefined). for (var i = 0; i < 3; i++) { // i visible here. } // i visible here. // let i = 0 makes i only visible in the for loop. let a = 1, b = 2; // Possible to declare multiple variables in one line.
-
-
undefined
vsnull
vsisNaN()
:undefined
is used for:- Unassigned/ Uninitiated variables;
- A missing property for an object;
- A missing value for an array;
null
is used for uncreated objects (like.getElementById()
’s returned value;)NaN
is a number that can’t be represented (0/0).
-
===
is the strict equality check (both the type and value) while==
is not strict. -
===
between two object references will be true only if they refer to the same object. -
Strings:
1 2 3 4 5 6 7 8 9 10 11
// There's no character type in JS. 'hello'.length; // 5 'hello'.charAt(1); // 'e' 'hello'.toUpperCase(); // 'HELLO' '1' + 2 + 3; // '123' 1 + 2 + 3; // '33' '' + 2; // '2'. A useful way to convert to string. 'A' === '\u0041'; // \ is the escape character. const foo = 42; `The answer is ${foo}`; // Back ticks give us template literals. They also allow multiline strings. // JS uses UTF-16, which is not enough for all the symbols. So a single 16-bit code unit is used for common characters but two code units are used for rare ones.
-
Like Python, JavaScript also has truthy and falsy booleans:
- false: false, 0, ‘’, NaN, null, undefined.
- true: all the others.
-
Two other ways of for loops:
1 2 3 4 5 6 7 8 9
let list = [4, 5, 6]; for (let i in list) { console.log(i); // "0", "1", "2". Indices only. } for (let i of list) { console.log(i); // "4", "5", "6". Values }
-
Arrays are a special type of object:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
let array = ['dog', 'cat', 'panda']; array.length; // 3 array[100] = 'fox'; // Create a sparse array. array.length; // 101. typeof array[90]; // undefined. array.push('new_item'); let new_item = array.pop(); // new_item. array.forEach(element => console.log(element)); delete array[1]; // Because arrays are really objects. typeof array; // object. let foo = array.concat([1, 2, 3]); // ['dog', 'cat', 'panda', 1, 2, 3]. let bar = array.join('#'); array.reverse(); array.sort(function (a, b) { return a - b; }); array.slice(1, 3); // Like in Python. let squared = array.map(a => a * a); let filtered = array.filter(a => a.length > 3);
-
/**/
can also occur in regular expression literals, so it’s recommended to use//
instead. -
regexp.test(string)
returns true if the string matches.let re1 = /abc/;
andlet re1 = new RegExp('abc');
are equivalent. -
Destructuring (similar to Python and Kotlin):
1 2 3 4
const x = [1, 2]; const [y, z] = x; // y = 1, z = 2. let a = 1, b = 2; [a, b] = [b, a]; // Swap like in Python.
-
Exceptions:
1 2 3 4
throw new Error('Foo.'); try { } catch (e) { }
-
Maps are better than objects for key-value pairs, as prototype properties are also present in objects. And objects can only have strings as keys.
1 2 3 4 5 6
let ages = new Map(); ages.set('Bob', 1); ages.set('Jack', 10); ages.set('Kate', 0); ages.get('Bob'); // 1. ages.has('Kate'); // True.
-
'use strict;'
at the top of a file or a function body enables the strict mode to catch errors. -
import ordinal from "ordinal";
is the ES module syntax for importing.
Numbers
-
Two built-in numeric types:
Number
andBigInt
. Integers are implicitly floats (64-bit likeDouble
):3 / 2 = 1.5
. This is good as short integer overflows are avoided. -
parseInt()
andparseFloat()
:1 2 3 4 5
parseInt('123'); // 123 parseInt('11', 2); // 3 parseInt('hi'); // NaN parseFloat('1.2'); // 1.2 parseFloat('123.2abc'); // 123.2. Parse until invalid char.
-
NaN
:1 2
Number.isNaN(NaN); // true. It's only true when the parameter is truly NaN. // The global isNaN gives unintuitive behavior, do not use!
-
Infinity
:1 2 3 4
1 / 0; // Infinity - 1 / 0; // -Infinity isFinite(Infinity); // false isFinite(NaN); // false
-
1e2
is the same as 100. -
Math.floor()
converts a number to an integer.
Functions
-
JavaScript has first-class support for functions. Functions are linked to
Function.prototype
, which is self is linked toObject.prototype
. -
The named parameters are more like guidelines:
-
Calling a function without enough parameters gives undefined.
-
Calling a function with more parameters than expected ignores the extra parameters.
-
arguments
is an array-like object holding all the values passed in. The rest parameter syntax...args
is preferred for ES6. We can also pass in an array with the spread operator...numbers
.1 2 3
function foo(...args) { for (let arg of args) {} }
-
-
Anonymous functions:
1 2
const foo = function() {}; // Equivalent to function foo(). // This is called a function expression, which is not hoisted (unlike function declaration).
-
Arrow function expressions are compact alternatives to traditional functions, but they can’t be used in all situations.
1 2 3 4 5 6 7 8 9 10 11
const animals = ['cat', 'dog', 'panda']; console.log(animals.map(animal => animal.length)); // Multiple arguments or no arguments require the parentheses. (a, b) => a + b; () => console.log('No arguments.'); // Multiline body requires braces and return. (a, b) => { let foo = 42; return a + b + foo; } let foo = a => a + 100; // Named function.
-
JS has by default function scopes instead of block scopes, which means a variable defined anywhere in a function is visible everywhere in the function. This also causes it to be better to declare all the variables in a function at the top instead of as late as possible. (ONLY applicable to
var
instead oflet
.) -
function.apply(thisArg, argArray)
setsthis
with the first parameter.
OOP
-
Objects are like Dictionaries in Python and HashMaps in Java.
-
let obj = {};
is the preferred way to create an empty object. -
Classic prototype-based example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
let foo = { name: "bar", coding: function() { this.name = "coding bar"; alert.log("I'm coding now."); }, age: 17 }; foo.height = 190; // This adds a new property. console.log(foo["name"]); // Also works, though the dot notation is preferred. delete foo.age; // This deletes the property. for (let prop in foo) { // Print all properties. console.log(prop + ": " + foo[prop]); } let bar = foo.bar || "default"; // || gives the default value, foo.bar.foobar; // Throw TypeError, as foo.bar is undefined. foo.bar && foo.bar.foobar; // Prevent TypeError.
-
We use functions to define custom objects and prototypes to add methods. A prototype is an object shared by all instances of that class. This is similar to extension functions in Kotlin and Swift. We can do the same for built-in objects (augmenting types).
1 2 3 4 5 6 7 8
function Person(first, last) { this.first = first; this.last = last; } Person.prototype.fullName = function() { return this.first + ' ' + this.last; }; let person = new Person('foo', 'bar');
-
Delegation is used for prototype chaining. A retrieval looks up the property in the object, then the prototype, then the prototype’s prototype, and eventually
Object.prototype
. -
typeof
looks up the prototype chain. We can usehasOwnProperty()
to check if the object has a property without looking up the chain:flight.hasOwnProperty('constructor')
. -
Classes are built on prototypes, so they are also special functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
// Class declarations are not hoisted. class Rectangle { constructor(height, width) { this.height = height; this.width = width; } // Static can't be called with an instance. We must use the class name. static displayName = "Foo"; static bar() { console.log("I'm bar"); } get area() { return this.calcArea(); } calcArea() { return this.height * this.width; } }; const square = new Rectangle(10, 10); console.log(square.area); square.displayName; // undefined. Rectangle.displayName; // Correct! // Class expressions are hoisted. let Rectangle = class { constructor(height, width) { this.height = height; this.width = width; } }; class Square extends Polygon { constructor(length) { super(length, length); // Note: In derived classes, super() must be called before you // can use 'this'. Leaving this out will cause a reference error. this.name = 'Square'; } get area() { return this.height * this.width; } }
-
a instanceof b
is the same as Python’sisinstance(a, b)
.
DOM (Document Object Model)
-
document.getElementById("foo").innerHTML
gives the content of the html element with the id ‘foo’. JavaScript does this by interacting with the DOM. -
foo.setAttribute("class", "bar")
sets the attribute andvar text = document.getElementById("bar").getAttribute("alt")
gets the ‘alt’ attribute.
Handling events
-
onclick
:1 2 3 4 5 6 7 8 9 10
const image = document.getElementById("foo"); image.onclick = bar(); function bar() { let img = document.getElementById("foo"); img.src = "new.png"; // We can change the property when we have the element. } const images = document.getElementsByTagName("img"); // Get a bunch for (let i = 0; i < images.length; i++) { images[i].onclick = bar(); }
Pitfalls
-
typeof null
givesobject
. A better null test is simplyfoo === null
.typeof []
also givesobject
.typeof NaN
givesnumber
. -
parseInt('16 tons')
returns 16 without warnings. -
NaN === NaN
returns false. -
Always use
===
and!==
to avoid surprises. -
Bitwise operations in JS are very slow and far from the hardware, as JS doesn’t have integers and have to to conversions.
-
JS declarations are hoisted. This means a variable with
var
can be used before the declaration. Hoisting moves all declarations to the top of the scope. -
If we forget to use
new
,this
is bound to the global object and the function will clobber global variables. -
Avoid
void
.
JSON
-
JSON strings use double quotes. All property names are strings. No functions or comments allowed.
-
JSON.parse()
converts JSON into a JS object.JSON.stringify()
converts a JS object to a JSON string.
Asynchronicity
-
Promise
is likeFuture
in Java.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
const promise2 = doSomething().then(successCallback, failureCallback); // Chaining: doSomething() .then(result => doSomethingElse(result)) .then(newResult => doThirdThing(newResult)) .then(finalResult => { console.log(`Got the final result: ${finalResult}`); }) .catch(failureCallback); const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve('foo'); }, 300); }); // Promise.all rejects immediately when any input rejects. var p1 = Promise.resolve(3); var p2 = 1337; var p3 = new Promise((resolve, reject) => { setTimeout(() => { resolve("foo"); }, 100); }); Promise.all([p1, p2, p3]).then(values => { console.log(values); // [3, 1337, "foo"]. });
-
async
andawait
are like in Dart. Async functions always return a promise.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
async function foo() { // This returns a different reference. return 1; } // is similar to function foo() { // This returns the same reference. return Promise.resolve(1); } async function foo() { await 1; } // is equivalent to function foo() { return Promise.resolve(1).then(() => undefined); } // Rewrite chaining: function getProcessedData(url) { return downloadData(url) // returns a promise .catch(e => { return downloadFallbackData(url); // returns a promise }) .then(v => { return processDataInWorker(v); // returns a promise }) } async function getProcessedData(url) { let v; try { v = await downloadData(url); } catch (e) { v = await downloadFallbackData(url); } return processDataInWorker(v); }
References
-
Head First JavaScript Programming: A Brain-Friendly Guide, 1st Edition
-
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
-
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
-
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
-
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function