JavaScript basics

Var, Data types & Ops

Var

Data types

  • dynamic typed, 7+1 basic data types
  • typeofnull: “object”, alert: “function” ..

Number

  • int/float & Infinity, -Infinity, NaN ..
    • 64-bit — Imprecise calc & BigInt ..
    • repre: e, hex/bin/octal — parseInt/-float(), toString(base)
    • regular num & NaN test: isFinite(), isNaN() vs Object.is()
    • rounding: multiply-and-divide & toFixed(n)
    • check for int: Math.round(n) == n ..
  • auto type convert: unary +, math-op/fn & Number(value)
  • tasks: Why 6.35.toFixed(1) == 6.3?, random int from min to max

String

  • quotes & ...${...} .., special chars: \n, \ ..
  • auto type convert: in output e.g. alert(..), toString(): obj as key, JSON.stringify() & String(..) ..
  • substr str.slice(), str compare str.localeCompare(str2), str.repeat(n), charCode String.fromCodePoint(code) & str.codePointAt(i) (in generate pwd ..) ..
  • task: Truncate text

Bool

  • 6 falsy values ..
  • auto type convert: logical op !!, compareson & Boolean(..)

null & undefined

  • null : value unknown;
  • undefined: init val for unassigned-var & default fn-return
  • Nullish coalescing op ??

Ops

  • compare diff type (convert to num) & null == undefined , NaN != NaN & ===
  • = .. & , ..

Function

Fn creation ..

  • fn dec: block-level (if strict), def-hoisting (start with function, require a name & cannot be called immediately ..)
  • fn-expr: easy dynamic def
  • arrow-fn (see obj)
  • new Function([...params]: ...str, body: str): create fn dynamically at run time (golbal scope) ..eval(code: str) ..

Fn is obj
Fn: a callable action (instead of data ..) obj ..

Closure

  • block scope & Lexical Environment (EnvRecord & outer-ref, created on run time) …
  • LE var/fn pre-populate & var “dead zone” ..
  • access var through LE chain (inner-outer) .. & updated on the LE it lives ..

Closure: a fn that remembers its outer vars and can access them by e.g. return a fn (or as obj prop) ..

Obj, Arrow Fn, Con-Fn

obj — str/symbol: any type

  • key can be reserved word, non-str/symbol key ==> toString ..

syntax

  • key existence test: in op & optional chaining syntax ?. (check null/undefined)
  • key iterate: for..in loop & prop order ..
  • value access: . vs obj["prop"] ..
  • prop add/set: obj.prop = xxx, delete: delete obj.prop ..
  • computed prop, prop value shorthand ..

obj deep cloning: for..in & typeof ..

tasks Check for emptiness

Methods & “this”

method def: fn-expr shorthand || copy from fn ..

this eval at run-time — losing “this” problem

bind “this”

Obj & Fn

Arrow fn

(...args) => expr || { ...body } || (obj-litiral) ..

  • no own this (cann’t run with new) 、super .. & arguments (no own context .., take from outer LE) ..

F: Constructor fn

  • set props & methods this.xxx = ...
  • call by new op (execute “internal algorithm”)
    • return this || another obj (prim-return ignored)
    • Constructor mode test: new.target
  • singleton obj new function() { ... }

Built-in Data Structure

Array

  • value indexing: arr[i], value iter: for..of || arr.forEach()
  • arr.length = greatest index + 1, init as item #, writable
    • manually increase: undefined values ("" in toString())
    • manually decrease: elem lost — clear arr arr.length = 0
  • delete arr[i]: value => undefined (length keep same) ..
  • arr.toString() => _,_,_...
  • Object.keys/values/entries(obj) => arr (enum & non-symbol key)

Methods ..

  • splice(..) : […rmed-elems]
  • filter(fn): […matchedItems], find(fn), indexOf(), includes()
  • map(fn) & reduce(..), sort(..) & reverse()
  • arr.join(glue=',') & str.split(delim) ((''): char[])
  • Array.isArray(value)

Iterable & array-like

  • iterable (e.g. arr & str) can be used in for..of ..
  • array-like: obj with numeric index & length ..
  • str both iter & arr-like ..str[i] & str.length
  • iter/arr-like => array: Array.from(obj, ..)

...: Spread syntax & Rest param

  • Spread syntax: ...iter: item list (use for..of internally)
    • copy/merge/convert by [...iter] / {...obj}
  • Rest params: ...arr in fn-def
    • fn can be called with any # of args (only args assoc with param will be count) ..
    • the old arguments (both iter & arr-like) ..

Destructuring assignment syntax

  • Copying iter-items into vars: let [...vars]/{...obj-props} = arr/obj (keep origin unmodified)
    • ignore arr-item by extra , .., : for prop-var rename .., = for var/obj-prop-var default value (unprovide value in right-side as undefined) ..,
  • easy swap: [guest, admin] = [admin, guest];
  • nested desructing : same structure (no var for the group one)
  • Smart fn params: pass obj as fn param
  • obj destcructing to existing var: ({…}) ({} in main code flow as code block) ..

tasks

JSON

  • Send data: JSON.stringify(obj, ..) & obj.toJSON(): formatted str (aka serialized obj)
  • Get data: JSON.parse(str, ..): obj

Prototypal inheritance

[[Prototype]] & prototype

  • [[Prototype]]: null || obj (aka prototype) .., accessor methods & fully obj-clone ..
  • the deprecated __proto__ prop .., reside in Object.prototype .. (why depr & “Very plain / pure dict” obj ..)

Prototypal inheritance: prop not exist in the obj -> find from proto (follow [[Prototype]] ref chain) ..

  • proto only for reading prop, write/delete data prop act on the obj (write may call setter in proto) ..
  • proto search opt ..
  • this in proto method: ref to obj bf . (method shared but obj state not) ..
  • inherited prop in for..in (if enum) but not in Object.keys/values() , diff by obj.hasOwnProperty(key) ..
  • task: add toString() to the “very plain” obj

F.protoype: a “normal” prop of con-fn F, set [[Proto..]] for obj from F when new F()

  • let obj = new F(): set obj.[[Prototype]] = F.prototype ({constructor: F} by default)
  • so obj.constructor === F & let obj2 = new obj.constructor()
  • re-assign F.protoype to another obj: prev created obj.[[Prototype]] not affect
  • native F.prototypes
    • Object.prototype: inherited to all obj, ref to a huge obj with toString and other methods
      • Object.prototype.__proto__ = null
    • Built-in obj (e.g. obj, arr, fn, num 5): store data in obj itself, store methods in prototype .. & ..
  • Check prototype chain by console.dir(obj) ..
  • task: Add decorating “defer()” to fns, the diff btw calls

Class

1
2
3
4
5
6
7
8
9
class F {
[static] field = ..; // access field in method by "this.xxx"

constructor(..) { /*..*/ } // called when "new"

[static] method() { /*..*/ }
[get/set] method(..) { /*..*/ }
[expr]() { /*..*/ } // computed name
}
  • bind this in method ..
    1
    2
    xxx = () => {..} // field store in obj itself ("this" = obj)
    this.xxx = this.xxx.bind(this); // bind existing method in constructor
  • access static outside F: F.xxx (F as this) .., ..
    • return obj in static method by new this(..) (factory method)
  • Class Encapsulation ..
    • protected: _xxx (convention) — proxy ..
    • private: #xxx (proposal, cannot access by this['#xxx'])
    • read-only prop: only getter no setter (set value on con)
  • task: class Clock

Class vs fn

typeof F: "function" ..

  • class F { .. }: F (con-fn) & F.prototype ..
    1
    2
    3
    4
    5
    6
    F: {
    constructor code,

    ...statics,
    [[Prototype]]: ParentF || Function.prototype
    }
    1
    2
    3
    4
    5
    6
    F.prototype: { 
    ...methods, // non-enum

    constructor: F,
    [Prototype]]: ParentF.prototype || Object.prototype
    }
  • new F(): obj
    1
    2
    3
    4
    obj: {
    ...fields,
    [[Prototype]]: F.prototype // access to non-static methods
    }
  • class & fn diffs ..

Class inheritance

extends set 2 proto refs: ..

  • F1.[[Prototype]] = F2 (con-fn inheritance, for statics)
  • F1.prototype.[[Prototype]] = F2.protptype (for methods)

Member overriding

  • override con/method: super(..) & super.method(..) ..
    • default child-con: super(...args) ..
    • call super(..) in child-con bf using this .. (why?)
      • when new: child-con create this by parent-con (in regular fn, an empty obj created for this) ..
      • method.[[HomeObject]] = their class/obj .. (bound forever ..) & super find parent methods in [[HomeObject]].[[Prototype]] ..
      • so method with super not free (it uses [[HomeObject]]) ..
      • super only work for method(), not method: function() { .. } ..
  • override field: field = .. (parent-con use own field value, not overriden one) ..
  • task: Extended clock, Class extends Object?

Extending built-in classes ..

Class Type Checking ..

  • obj instanceof FF[Symbol.hasInstance](obj) & F.prototype.isPrototypeOf(obj)
  • Object.prototype.toString: [object xxx] (xxx: value of obj[Symbol.toStringTag])

Mixin

A class / obj contains methods for other classes without inheritance

  • imp by copying method into prototype Object.assign(F.prototype, mixin) (prevent overwrite)
  • EventMixin example

Error handling

Error obj: new Error/Syntax*/Reference*/Type*(msg) ..

  • props: name(F): message e.g. “Ref..Error: xxx is not defined” in alert ..

try..catch..finally: sync & run-time err / exception ..

  • throw err (in try || rethrow in catch): throw
  • checking err: instanceof ..
  • Global handler: window.onerror ..

custom err/ extending err: ValidationError .. & PropertyRequiredError example ..

  • set name: this.name = this.constructor.name;
  • wrapping-exception pattern: catch syntax & validation errs and throws ReadError ..