JavaScript miscellaneous

Chrome console & Debugging

Chrome console .. & debuging in chrome ..

  • logging with console.log(...)
  • breakpoints & debugger cmd, execution tracking …

‘use strict’

To enable ES5

  • off by default — always put 'use script' on top of script/fn-body
    • auto enable for class & module
    • no way to cancel once enter strict mode
  • non-strict: assignment to non-existing var (without declear) ==> create a new global var (for compatibility) ..

Data types

  • Object key: str/symbol (keyed values collection)
  • Array key: int (ordered collection, MDN)
  • Map key: any type
  • Set: unique values

Date & time

Map & Set, WeakMap & WeakSet

Map: obj with key of any type, key compare: === & NaN === NaN

  • new Map([ [_,_], [_,_] ..]) & obj <=> map: new Map(Object.entries(obj)) .. & Object.fromEntries(map) ..
  • iter: forEach(..), keys/values/entries() ..

Set: unique values

  • methods similar to map, get/set() => add(), iter by insertion order ..
  • Filter unique array members: Array.from(new Set(arr))..

WeakMap/WeakSet: obj unreachable by other means -> rmed from memory & WM/WS

Obj advanced

Symbol type

unique (even same desc) vs global symbol (1 key 1 symbol)

1
2
3
alert(Symbol("id"));    // TypeError (not auto-convert to string)
alert(id.toString()); // Symbol(id)
alert(id.description); // id

Obj prop config

prop descriptor: value + writable, enumerable, configurable ..

  • get/set desc: Object.getOwnPropertyDescriptor(), Object.defineProperty() & clone with desc ..
  • obj created “the usually way”: flags default true; flags not supplied in desc defalut false
  • built-in toString() non-enumerable
  • Sealing an object globally …

Accessor get/set xxx([value]) {..}, trigger when xxx prop read/assign

  • desc: get/set([value]) {..}, enumerable, configurable
  • smarter g/setter: store value in another internal prop
  • age -> birthday example ..

Key iter

  • method differs (all return an arr) .., ..
    • Reflect.ownKeys(obj)/obj.hasOwnProperty(key): own
    • Object.getOwnPropertyNames: own, non-symbol — Object.getOwnPropertySymbols(obj): own, symbol
    • Object.keys/values/entries(obj): own, non-symbol & enum
    • for..in: own + proto, non-symbol & enum
    • Object.assign(): symbol + non-symbol ..

Global obj

Provide built-in var & fn (can be access directly), e.g. window in browser & global in node

  • e.g. window.alert, window.innerHeight & global fn || var-var
  • make value global: window.xxx = ... (props of global-obj)

Primitive as Obj

provide methods to op prim but keep them fast & lightweight e.g. String, Number, Boolean, Symbol

  • temporary wrapper obj created & destroyed .. — manually modify obj wrap => err (strict) || undefined ..

Obj to primitive

  • 3 hints: "string", "number" & "default" (no boolean hint: all obj => true)
  • 3 methods: must return a prim value ..
    • [Symbol.toPrimitive](hint) return obj ==> err
    • built-in toString() => “[object Object]”, valueOf() => obj itself (ignored) ..

JS engine internal: Event loop

Macrotask queue: handle tasks e.g. current rgl sync script, external script load & exe, UI/network event handler (e.g. mousemove), setTimeout callback

  • tasks come while engine busy — enqueued to the queue
  • re-render only af task complete — if current task tasks long time: Page Unresponsive alert
  • enqueue a macrotask: setTimeout(f)
  • use case: split CUP-heavy task by setTimeout => react other tasks in-btn / progress indication

Microtask queue (aka PromiseJobs queue): handle async promise handlers in .then/catch/finally & await

  • handler enqueued when promise settled, run af current code & prev micro finished ..
  • global unhandledrejection event: triggered when microtask queue completed but exist “rejected” promise
  • make code exe af .then/catch/finally: chained by .then()
  • enqueue a microtask — queueMicrotask(f) ..

JS exe-flow based on event loop: engine sleep until tasks appear, then exe from the oldest one (FIFO) ..

  • exe process (no macrotask in-btw microtask — ensure app-env same) ..
    1. current rgl sync script (=> all microtasks => render)
    2. some macrotask (=> all microtasks => render)
    3. another macrotask
  • cpu-heavy event & web worker: another thread

Popup win & iframe

Popup win: window.open(URL, ..) .. — open a new tab (by default) in browser (or win if sizes provided ..)

  • win <-> popup: let popupWin = window.open(..); .., popupWin.opener ..
  • close popup: win.close() & win.closed (status check) ..
  • win focus/blur: win.focus/blur() & win.onfocus/onblur ..
  • win scrolling & resizing

Embedded win: <iframe ..>iframe.contentWindow/contentDocument ..

  • wrong doc pitfall: iframe.contentDocument when iframe.onload (wrong for the one immediately af iframe created) ..
  • window.frames (named coll) & win hierarchy (iframe contains iframes) — check whether iframe by win === top ..
  • iframe sandbox attr (exclude certain actions inside iframe)
  • transparent iframe & clickjacking attack (intercept a single click ..)

Bin data, files

meet binary data: files/img (create, upload, download) ..

BufferSource

  • ArrayBuffer(byteLength): store raw bin data (byte-seq) — read data by 2 kinds ArrayBufferView ..
    • mtp typed arrays : choose data format at constructor, e.g. Uint8Array .. — “out-of-bounds” problem .. & methods ??? , task: Concat typed arrays
    • DataView: choose data format at method, e.g. dataView.get/setUnit8(..)
  • BufferSource: ArrayBuffer + ArrayBufferView ..
  • str <=> bytes: textEncoder.encode(str) (utf-8) & new TextDecoder(encoding, ..).decode(BufferSource, ..) ..

Blob

Blob bin data type : file up/download op & web network request ..

  • Blob type 'text/html | text/plain'Content-Type in network requests
  • Blob → URL (for <a>/<img>)
    <a download="hello.txt">, a.href = URL.createObjectURL(blob)
    a.click(), URL.revokeObjectURL(a.href)
  • img → Blob (via <convas>)

File & FileReader

  • File: inherit from Blob, get from input.files[0] & Drag’n’Drop events ..
  • FileReader: read data from Blob via fr.readAs*(blob) (async), load/error events & fr.result / error ..
  • sent file over network: Fetch/ XHR ..

URL object

  • new URL(url: str, [base]), url components, char encoding

Web Components

Custom HTML elem

  • all-new: extends HTMLElement .. (no semantics ..)
    • 2-step creation: JS class + customElements.define(tag, js-class)
    • <time-formatted> & Intl.DateTimeFormat(..) demo …
  • customized: extends built-in elem (with semantics) — 3-step creation (<button is="my-button">)
  • html tag <my-element> / document.createElement(..): create instance of MyElement ..
  • CSS selector :not(:defined): style “undefined” elems, e.g. custom html-tag bf customElements.define(..) ..
  • HTML parser process parent bf child
    • access childElem by setTimeout(..) (af html parsing completed)
    • want outer callback trigger af inner elem ready: inner elem dispatch events & outer listen
  • task: Live timer elem

Shadow DOM

  • Shadow tree (for encapsulation: own id/quary spaces, styling .., ..) vs Light tree
    • create by elem.attachShadow(..) => shadow root: elem.shadowRoot
      • elem.shadowRoot.host: the elem, shadow tree host
      • shadow root like elem (innerHTML | DOM methods appliable), but cannot have event handler (use its 1st child) ..
    • if elem has both -> only render shadow — compose by slot
      • <slot name="..."> (shadow) & elem with slot="..." (light, render as child elem of <slot>)
      • slotchange event (when slotted-elems add/rm/replace) , .. & slot-related methods
      • open/close menu example
  • styling: :host*, ::slotted, components custom CSS props & pseudo attr ..
  • event handling: event happen in shadow DOM hidden to outside — retarget (bubble) to its host elem ..
    • evebt bubbling: e.composedPath(), e.composed (true for bubble across shadow DOM boundary) …

Template elem

<template> tag: for HTML markup storage (any syntax-valid HTML), content ignored by browser ..

  • elems not render, style/script not work, until tmpl.content: DocumentFragment .. inserted to doc

Generator

Regualr fn retur 1 value ==> Generators “yield” mtp values 1-by-1 (allow create data stream)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function* generateSequence() {    // generator function
yield 1; // no value af 'yield' means 'undefined'
yield 2;
return 3;
}

let generator = generateSequence(); // when called, return a Generator instead of run the fn
alert(generator); // [object Generator]

// call generator.next() to get result
{value: 1, done: false}
{value: 2, done: false}
{value: 3, done: true} // done:true for "return"
{done: true} // no more value af 'return'
  • when called, it return a Generator (an Iterator with next() method) to manage the fn-exe
  • gen.next() — run fn-exe until yield <value> stmt & return {value: <value>, done: true/false}
  • pass data back to gen.yield
    • gen.next(arg) — pass arg as the value of current yield & return the resulting-obj of next yield ..
    • gen.throw(err) — throw err as the result of current yield (err not catched in gen() will fall out to gen.throw)
  • Generator composition — embed gen-values into another gen-fn by yield* gen(...)
  • Task: pseudoRandom(seed)

Async iteratable async *[Symbol.asyncIterator]()

Programming pattern

Fn decorator ..

Currying

  • transform f(a, b, c)f(a)(b)(c) — easily for partial (see below): let fn = f(a), then fn(b)(c) ..
  • implementation ..

Proxy & Reflect
Proxy: a wrapper obj that intercepts ops (e.g. get/set props) on the target-obj by its handler (obj with traps) ..

  • ops on proxy => trigger crp trap => (if no) forwards ops to target ..
  • proxy no own props, access from target ..
  • ops -> proxy handler methods -> internal methods (low level)
  • Proxying a getter with receiver by Reflect.get(target, prop, receiver);: pass receiver (correct obj) as this in getter

Reflect: to complement Proxy

Tasks: array[-1], makeObservable(target)

Recursion ..

DOM

  • range & selection ..
  • mutation observer: fire callback when change observed on a DOM node, childList & subtree, attributes, text content ..
    • div contentEditable attr

Module

  • a script file that load each other by export & import .., intro in ES6 ..

module in browser: <script type="module"> ..

  • type attr enable import/export .., .. — the old “nomodule” attr ..
  • module defer .. by default .., can async .. (work for both inline/external scripts .., ..)

export/import syntax ..

  • default export (import without {}) vs named export ..
  • default keyword for default export (when import, any name works — naming consistency: crp to file name ..)
  • as for export/import renaming
  • Re-export: export .. from .. (import & export): single entry point of fnality (mtp re-export stmt in 1 file) ..
  • import "module": run module code without assign to var

dynamic imports import(<module-path>): return a promise resolves into a module-obj with exports (as keys) ..

  • a special syntax, not a fn (like super(), cannot copy & use call/apply)
  • work with rgl <script> tag (no need type=”module”)

module vs rgl script ..

  • auto strict (this == undefined)
  • own scope (interchange fnality via import/export ..)
  • exe only once & export share when imported mtp times (use as init)

others

  • place of import/export stms not matter (script top or bottom), but not work inside “{…}” — dynamic imports ..
  • import.meta: obj contains info about the current module ..
  • build tool (for deployment) e.g. webpack (bundle modules together & tree-shaking) ..
    • resulting “bundled” script bundle.js no import/export stmt ==> no type=module required (rgl script src="bundle.js") ..

Other concepts & Resources