I recently discovered "Eloquent JavaScript" by M. Haverbeke which seems to be a common introduction to both programming and JavaScript, e.g. for bootcamp students. So far, I followed a conservative philosophy of "the least amount of JS you can get away with" and thus my JS skills are limited:
- Static page tools: Lifetime Clock, Kitchen Cleaning Plan (somewhat modular, with tests run in browser)
- Minor accessibility features in vanilla JS used in ggpy: https://github.com/ooz/templates/blob/master/html/oz-accessibility.js
- Unoptimized, non-modular Electron app using React.js: https://github.com/ooz/flow-charter
- Unoptimized, non-modular game jam games using Phaser: Ogre Forge, home is, Handshake
Study goals:
- Level-up JS skills, understand prototypic features
- Learn and write modern JS instead of a mix of various language features/standards
- Understand JS module system, dependency management, bundling and webpack
- Get started with Node.js, learn basic API
(As a person with extensive programming and CS background, ymmv.)
There is only one value in JavaScript that is not equal to itself, and that is NaN (“not a number”). NaN is supposed to denote the result of a nonsensical computation, and as such, it isn’t equal to the result of any other nonsensical computations.
- For beginners, the author recommends to always terminate statements with semicolons, because omission rules are non-trivial.
letdefines a binding (aka variable)- There's no reason to use
varoverletin modern JS - Write JS in camelCase
-
Three ways to define functions:
const hello1 = function(name) { return "Hello " + name; }; const hello2 = (name) => { return "Hello " + name; }; function hello3(name) { return "Hello " + name; }
hello1andhello2use binding notation (binding a function value to the respective names)hello2uses arrow syntax, which is the same ashello1for the most part (difference revealed in chapter 6: arrow functions do not bindthis, but seethisof the surrounding scope)hello3-style function declarations are read before the JS program is executed, so the function can be used before it is defined.hello1andhello2need to be defined before their usage- Also mind the semicolon terminating
hello1andhello2definitions
-
If too few arguments are passed, the affected parameters become
undefined -
Avoid recursion, JS has no tail nor optimized recursion
-
Two ways to access a property:
name.prop; name[prop]; // Expression "prop" is evaluated and the property with the resulting index/name accessed
- The second way may be used to work around invalid property names e.g. starting with numbers
-
Use
Object.keys(myObj)to get all property names of objectmyObj -
Use
Object.assign(left, right)to copy all properties fromrighttoleft -
==checks for identity (reference to same object), not (deep) equality of contents
(nice phi coefficient example to measure correlation between two variables)
array.includes(elem)checks whetherelemis inarray- Further array methods:
shift(pop first element),unshift(insert at beginning of array),indexOf(find first),lastIndexOf(find last),slice,concat - For strings,
indexOfcan also search for substrings with length greater than 1 - String padding with
padStart - Further string methods:
trim,split,join,repeat - Rest parameters and spread operator
... - Re-using a name for a binding defined with
letorconstwill trigger a warning, but not forvarorfunction! - Destructuring arrays and objects
JSON.stringifyandJSON.parse
forEach,filter,mapandreduceare built-in.reduceis clever with the start elementsometests whether at least one element satisfies the given predicateeverytests whether all elements in an array satisfy the given predicatefindIndexreturns the index of the first element satisfying the given predicate- JS strings use UTF-16 encoding
- Some problems when using string
lengthand index access.charCodeAtandcodePointAtare tricky, too
this:
thispoints to the object a method was called on- This works even for functions assigned to objects later. Using the
callmethod on a function, thethiscontext can be set explicitly - Arrow functions do not bind
this, but see thethisof the surrounding scope
-
Unknown properties are requested from an object's prototype and from the prototype's prototype etc.
-
The root prototype is
Object.prototype -
Object.getPrototypeOfgets the prototype of the passed object. The prototype of the root prototype isnull -
Functions derive from
Function.prototypeand arrays fromArray.prototype -
Object.createis used to create an object of a given prototype -
Shorthand for defining a method in an object (notice
speak):let protoRabbit = { speak(line) { console.log(`The ${this.type} rabbit says '${line}'`); } };
-
newis used to invoke a constructor function -
By convention, private property names start with
_ -
Capitalize constructor function names (again, matching other languages' class feature)
-
Class name is optional for class expressions. Q: what's the use-case for class expressions?
-
Overriding of properties can be done on prototype- and object-level (instance-level)
Maps:
-
Using normal objects as maps is dangerous, e.g. because they contain additional "keys" due to inheriting properties from
Object.prototype -
One solution is to create objects with
nullprototype and use them as maps:let map = Object.create(null);
-
Object property names must be strings. So when using an object as a map, only string keys are allowed
-
JS has a
Mapclass withset,getandhasoperations -
Object.keysreturns only an object's own properties, not the ones from its prototype -
The
inoperator checks for the property both in the object and its prototype -
The
hasOwnPropertymethod checks only for the object's properties, not those from the prototype
Polymorphism and Symbols:
- JS supports polymorphism. Objects fulfill an interface, if they have properties with matching names (or matching symbols)
- Each
Symbolis unique, even when created with the same name
Iterators:
- Objects fulfill the iterator interface when they have a method labeled with the
Symbol.iteratorsymbol - String iterator example, custom matrix iterator example
Getters, setters, static:
get,setkeywords define opaque getter/setter functions that look like regular field propertiesstaticfunctions are bound to the constructor (class name), not to the prototype
Inheritance:
extendkeyword extends a class from another prototype than the defaultObject.prototype- Use
superto call superclass constructor andsuper.propertyNameto use superclass propertypropertyName - Use
instanceofoperator to check whether an object has some class (constructor) somewhere in its inheritance hierarchy
-
Enable strict mode by writing the following line at the beginning of a file or function body:
"use strict";
-
Strict mode reports:
- Usage of undefined bindings (normally, they'd be created implicitly in global scope)
thiswill beundefinedin function scope when the function is not used as a method (normally,thiswould then refer to global scope)- Defining function parameters with the same name
- Removes
withstatement and other problematic language features
-
Calling constructors defined with
classkeyword withoutnewwill always result in an error -
Use TypeScript
Debugging:
- Inserting
debuggerstatement will pause program execution when browser developer tools are open
Exceptions:
-
Raising an exception:
throw new Error("message");
-
Handling an exception:
try { throwsException(); } catch (error) { } finally { // Is always run }
-
JS doesn't differentiate exception types (it's all just
Error) -
However, defining own error types by extending
Errorand checking withinstanceofis recommended
9. Regular Expressions (Summary)
-
Two ways to define RegEx patterns:
let regExConstructor = new RegExp("abc"); let regExLiteral = /abc/; let caseInsensitiveFlag = /abc/i; let constructorFlags = new RegExp("abc", "giuy");
-
Escaping rules vary by which definition style is used, e.g. in RegEx literals
/needs to be escaped, backslashes don't must not be escaped. In RegEx strings passed toRegExpconstructor, backslashes need to be escaped. -
testmethod checks whether a passed string matches the RegEx -
execmethod returns a match withindexproperty ornullif no match is found. A match object looks like the matched string -
String objects have a
matchmethod accepting a RegEx parameter that behaves like RegExmatch -
^matches start of string,$matches end of string,\bmatches word boundary -
.matches any non-newline character, if any character incl. newline needs to be matched,[^]can be used -
Repetition operators are greedy (match as much as possible) by default, non-greedy (match as little as possible) ones are followed by a
? -
Sanitize input before constructing RegExp objects from user input
Date:
- Month numbers start at 0, day numbers at 1
String replace:
- Accepts RegEx as first argument and even matched groups in the second argument. By default, just replaces the first occurence
- To replace all occurences, end RegEx with
gflag (similar toiflag above and other RegEx implementations) - The second argument may also be a function applied for every replacement
String search:
- Like
indexOf, but searching for first position matching pattern - Searching for
lastIndexis tricky,yis "sticky" flag (= RegEx must be found starting at lastIndex, by default: from beginning of string)
- Use
Functionas a safe alternative toevalif data needs to be executed as code requireandexportscome from CommonJS and is used by Node.js- Modern JS has
importandexportbuilt-in export defaultspecifies the module's main export which may not even have a name
ini and dijkstrajs packages from NPM are mentioned.
setTimeout(callback, delayInMs)
Promises:
Promise: object which is executed may produce a value at some time in the futurePromise.resolvewraps a value in a Promisethenmethod gets the value produced by the Promise, applies the passed callback function and produces another Promise. This way,thenand callbacks can be chained (without confusing callback nesting)Promiseconstructor accepts a function which is immediately called with a resolve function as an argumentPromise.rejectcreates a Promise failurecatchgets the reason why the Promise failed and returns a Promise, similar tothenfor the non-erroneous case- The
catchhandler may also be registered as the second optional argument tothen, for convenience Promise.all
paused at https://eloquentjavascript.net/11_async.html#h_o8Vlf60I8f
- Basics on TCP, client-server, URL structure, HTTP, HTML, how to include JS in HTML
- DOM, Trees
documentis the DOM root,document.documentElementthe enclosing<html>tag- Each DOM node has a
nodeTypeproperty childNodesproperty is ofNodeListtype stemming from DOM standardization- childNodes, firstChild, lastChild, previousSibling, nextSibling, parentNode visualized
childrenproperty contains only element child nodesnodeValueof a text node contains its text
- All element nodes have
getElementsByTagName, e.g.document.body document.getElementByIdgetElementsByClassName
remove,appendChild,insertBefore- Inserting/appending an existing node will first remove it and then insert it at the new position
replaceChilddocument.createTextNodeArray.fromdocument.createElement- Use
getAttributeandsetAttributein combination with custom attributes (e.g.data-something), standard attributes may directly be accessed as properties - The
classattribute is accessed withclassNameproperty orgetAttribute("class") offsetWidth,offsetHeight,clientWidth,clientHeightandgetBoundingClientRect,pageXOffset,pageYOffsetfor position/size of HTML elements
Styling/CSS:
- Accessing an element's
styleproperty - Rule precedence and specificity
- Query selectors and
document.querySelectorAll(returning a NodeList) anddocument.querySelector(returning just the first match) static,relativeandabsoluteposition explained- Animation using
requestAnimationFrame
addEventListener("click", handler)or use theonclickattributeremoveEventListener(handler)- Event handlers get passed an event object which differs by event
type
- By default, events on children will also trigger events on the parent nodes
- The more specific handler is triggered first and events propagate "outward"
- Call
stopPropagation()on the event object to stop propagation - Use
targetproperty of a event object refers to the node where the event originated focusandblurevents do not propagate,loadevent don't propagate either
Default actions, e.g. right click is context menu, down arrow is scrolling down
- JS event handlers are triggered before the default actions
- Call
preventDefault()on the event object
keydown,keyupevents.keydownalso fires again every time the key repeatsshiftKey,ctrlKey,altKeyandmetaKeyproperties- Use
tabindexattribute to enable focus for a DOM node
Event types
click,mousedown,mouseup,dblclick,mousemovekeydown,keyuptouchstart,touchmove,touchend,touchespropertyscrollfocus,blurloadevent on window and document.body objectsbeforeunload
- Use to offload long-running computations to separate threads
- Communication happens via messages
Workerclass andpostMessagemethod- Messages are JSON copies
Timers
clearTimeout(timer)to delete a timer created withsetTimeoutcancelAnimationFramesimilarly canceles the object returned byrequestAnimationFramesetIntervalandclearInterval- Use
setTimeoutto "debounce" rapidly firing events likemousemoveorscroll
18. HTTP and Forms
encodeURIComponentanddecodeURIComponent- Use
fetchto make HTTP requests - Various
inputtypes with examples:text,password,checkbox,radio, andfile. Textarea and select+option tags - Form fields fire a
changeevent when their value changes. For text fields, this is after the field loses focus.inputevent is fired for any character change - Use methods
focusto focus keyboard input on an element,blurto remove focus document.activeElementpoints to the currently focused element- Use
autofocusandtabindexattributes. Atabindex=-1skips over a normally focusable element - Form
submitevent - Use
FileReaderto read content of file(s) - Use
localStorageas client-side key-value store {[name]: ""}creates an object with the dynamic property namednamesessionStorageis likelocalStorageexcept it is cleared end of session (browser is closed)
20. Node.js
Read the entire docs at https://nodejs.org/en/docs/
process.exit(0),process.argv- "All the standard JavaScript global bindings, such as Array, Math, and JSON, are also present in Node’s environment. Browser-related functionality, such as document or prompt, is not."
readFile,writeFile
HTTP module
- Basic http server, basic http client, but use wrapper packages from npm instead
Streams
paused at https://eloquentjavascript.net/20_node.html#h_dJhdomfGgD