🌎
web.dev
  • 🌏web.dev
    • 🚧todo
    • 🧪lab
    • 🚸legend
    • 🗒️template
    • 🎹shortcuts
    • 📗reference
      • 📘JS spec
      • 📙javascript.info
      • 📙Eloquent JavaScript
      • 📙JavaScript: The Definitive Guide
      • 📗book
        • 📗You Don't Know JS: series (v.1)
        • 📗You Don't Know JS: series (v.2)
          • 📗YDKJS: Scope & Closures (v.2)
        • 📗Functional-Light JavaScript
      • 🔰website
        • 🔰freeCodeCamp
    • ❤️fav
  • ⚙️tools
    • 🔰tooltip
    • 🔰syntax highlighting
  • 🔰HTML
    • 🔰element
      • 🎛️<select>
      • <form>
      • 🏷️<hr>
      • <figure>
      • <input>
        • type="range"
        • type="radio"
    • block vs. inline
    • Semantic Elements
  • 🔰CSS
    • ⭐box model
      • height
      • border
      • outline
      • 🔸margin
        • layout with margin
        • default margin
        • 🔸negative margin
        • margin collapsing
      • 💊box-sizing
    • 🔰layout
      • 🔹position
        • 🔸positioned ancestor
        • 🔸nearest positioned ancestor
      • 🔹top
      • 🔹display
        • 🔸block
          • inset
        • 🔸inline
        • 🔸flex
          • 🚧Flexbox
            • flex-wrap
            • justify-content
            • align-content
            • flex
            • container properties
          • 🔸flex-direction
          • 🔸align-items
        • 🔸inline-block
        • 🔸inline-flex
        • 🔸none
      • 🔸formatting context
      • Layout System
        • Lorem
        • Spacer
        • VStack
        • HStack
        • Box
        • VCenter/HCenter
        • Dark Theme
      • ✨examples
        • Email Form
      • 🔰CSS grid
      • vertical-align
      • float
      • vertical centering
        • center with "transform"
        • center with "flex"
      • Block Formatting Context
    • 🔰values
      • 🔰units
        • 🔰相對單位
          • 🔸percentage (%)
          • 🔸rem
          • 🔸em
      • 🔰colors
        • HSL
      • 🔰gradients
        • radial-gradient()
    • 🔰variables
    • 🔰functions
      • 🔹calc()
    • 🔰selectors
      • select direct children by JS
      • basic selectors
      • attribute selectors
      • 🔰combinators
      • pseudo-class
      • 🔸pseudo-element
      • custom selectors
    • 🔰properties
      • get/set CSS props
      • Logical Properties
      • overflow
    • 💡Tips
    • CSS import
    • Responsive Design
    • CSS Rules
      • CSS reset
    • Design
      • 🔰CSS shadows
    • ✨examples
      • styling <details>
      • undefined tag
      • modal dialog
      • nav bar
      • glowing buttons
    • Cascade
      • Order
      • Specificity
      • Inheritance
    • Stacking Context
    • Font
      • Font Awesome
      • font-family
    • Text
    • List
  • 🍒JS
    • 💡tips
      • fill 2D array
    • 🧚technique
    • ⭐feature
      • ⭐️ ES5
      • ⭐️ ES6 (2015)
      • ⭐️ ES2017
      • ⭐️ ES2016
      • ⭐️ ES2018
      • ⭐️ ES2020
    • 🔰concept
      • 🔰execution context
        • 📘this
          • ❗"this" determined on call site❗️
        • 🔰running execution context
        • 🔰lexical environment
          • 🔰environment record
          • 🔸[[Environment]]
          • ❗lexical environment optimizaton
        • 🔰call stack
      • 🔰context
        • 🔰global context
        • 🔰class body context
      • 🔰environment
        • ⚙️JS engine
          • 🔰mode
            • 🛡️strict mode
              • 🛡️eval has its own scope in strict mode
              • ✅always use strict mode
            • ❗sloppy mode
              • ❗accidental global variable in sloppy mode❗️
              • ❗modify current scope at runtime❗️
            • ⚖️strict vs. sloppy
          • 🔰v8
        • 🔰Node.js
      • 🔰associated array
      • 🔰parser
    • 🔰compilation
      • 🔰lexing
      • 🔰parsing
      • 🔰code generation
      • 🔰JS is compiled
      • 🔰compile-time
      • 🔰runtime
    • 🔰grammar
      • 🚩directive
        • 📜'use strict'
      • *️token
        • 🗝️keyword
          • 🔐reserved word
            • ❗reserved word as property name is allowed❗️
          • 🔑contextual keyword
        • 🔣punctuator
          • *️hash (#)
          • 🔣semicolon (;)
            • 🔰automatic semicolon insertion
            • 🔰statements that must end with ";"
          • 🔣dot (.)
          • 🔣comma (,)
          • 🔣colon (:)
          • 🔣plus (+)
          • 🔣equal (=)
          • 🔣3 dots (...)
          • 🔣brackets []
          • 🔣braces {}
          • 🔣parentheses ()
          • 🔣question dot (?.)
          • 🔣question (?)
        • 🆔identifier
          • 🆔special identifier
          • ⚖️identifier vs. string
          • ⚖️identifier vs. keyword
          • ❗undeclared identifier
          • 🔰static binding
          • 🔰dynamic binding
        • ✡️literal
          • 🔰trailing comma
      • 🔰comment
      • 🈯declaration
        • ❗redeclaration
        • 🈯lexical declaration
      • 📜statement
        • 📦expression
          • 📦primary expression
          • 📦invocation expression:f()
            • 📦method invocation
          • 📦left-hand side expression
        • 🔁loop
          • 🔁while
          • 🔁do...while
          • 🔁for loops
            • 🔁for
              • 🔰"for-init" scope
              • 🔰"for-init" block
            • 🔁for-in
            • 🔁for-of
              • ❗arrays are iterated "live"❗️
              • 🔰for-of with objects
              • 🔰for-of with strings
              • ⚖️for-of vs. forEach
            • 🔁for-await
            • ⚖️for-in vs. for-of vs. in
            • 📘arr.forEach()
        • 🔀control flow
          • ⤴️jump
            • ⤴️return
            • ⤴️continue
            • ⤴️break
            • ⚖️continue vs. break
          • 🔀branch
            • 🔀if
            • 🔀switch
              • 🔀case clause
        • 📜other statement
          • 📜block
            • 🔰block scope
          • 📜label
          • 🛑with
          • 📜empty statement
        • ⛔statement expected❗️
      • 🚧operator
        • 🚧table of operators
        • 📖terms for operator
          • 🔰arity
          • 🔰associativity
          • 🔰lvalue
          • 🔰dividend
          • 🔰divisor
          • 🔰non-numeric operand
          • 🔰short-circuiting
        • 🔴assignment
          • ⭐destructuring assignment
            • 🔰destructuring array
            • 🔰destructuring iterable
            • 🔰object destructuring
            • 🔰nested destructuring
              • 🌟nested default values
            • 🔰destructuring arguments
          • ➕assignment (=)
        • 🔴arithmetic operator
          • ⭕unary arithmetic operator
            • 🚧unary plus (+)
          • ⭕binary arithmetic operator
            • ➕add/concate (+)
            • ➕division (/)
            • ➕remainder (%)
              • 💾modulo(a, b)
              • 💾mod(a, b)
              • ⚖️remainder vs. modulo vs. mod
            • ➕exponentiation (**)
          • ⭕bitwise operator
            • 🌟2's complement
            • 💾bitview()
            • ➕bitwise not (~)
              • 🔰double tilde (~~)
              • ⚖️(~~) vs. Math.trunc()
        • 🔴relational operator
          • 🔰equality & inequality operator
            • ➕sloppy equality (==)
            • ➕strict equality (===)
          • 🔰comparison operator
          • ➕in
          • ➕instanceof
        • 🚧logical operator
          • ➕nullish coalescing (??)
          • ⭕unary logical operator
          • ⭕binary logical operator
        • ⭕unary operator
          • ➕void
        • ⭕binary operator
          • ➕comma operator (,)
        • ⭕ternary operator
          • ➕conditional operator (?:)
        • 🔰spread operator (...)
        • ➕rest operator (...)
      • 🔰statement vs. declaration
    • 🔰scope
      • 🔰lexical scope
      • 🔰static scope
      • 🔰dynamic scope
      • ⚖️static vs. dynamic scoping
      • 🔰scope chain
      • ⚖️lexical environment vs. scope
      • 🔰top-level scope
      • 🌟global scope
        • 📘global object
          • 🏷️globalThis
          • 🍄window
            • 🔸window.name
            • 🔸window.navigator
          • 🍄global
          • 🔸eval
          • 🔸global object property
        • 💾isCurrentlyInGlobalScope()
        • 🔰global variable
          • 🔰global let
            • ❗global let shadows global object property
          • 🔰global var
        • 🔸DOM element with "id"
      • 🔰module scope
      • 🔰function scope
      • 🔰function name scope
      • 🔰parameter scope
      • 🔰hoisting
        • 🔰function hoisting
          • ❗function hoisting first❗️
        • 🔰variable hoisting
          • 🔰var hoisting
            • ❗function expression not hoisted
          • 🔰let/const/class hoisting
      • ⚖️context vs. scope
    • 💍value
      • 🔰type
        • 💍falsy
        • 📖terms for type
          • 🔰statically typed
          • 🔰dynamically typed
        • 🔰type conversion
        • 🔰type name
          • ➕typeof
          • 💾baseTypeName()
          • 💾typeName()
          • 💾custom type name
        • 🔰nested type
        • 👔type functions
          • 💾functionDefinition()
          • 💾isObject()
          • 💾isPrimitive()
          • 💾isFunction()
          • 💾isClass()
      • 🦠primitive
        • 💍nullish
        • 🔸undefined
        • 🔐null
        • 🔢Number
          • 🔰floating-point
          • 🔰integers
          • 🔴special number
            • 🔢Infinity
            • 🔢NaN
            • 🔢Number.EPSILON
            • 🔢Number.MAX_VALUE
            • 🔢Number.MAX_SAFE_INTEGER
          • 👔Number+ext
            • 💾n.toHex()
            • 💾n.isEqual()
            • 💾isNumber()
          • 👔Random
            • randomInt()
            • randomFloat()
          • 👔Quaternion
        • 🔢BigInt
        • 🦠Boolean
        • 🔤String
          • 🔰raw string
          • 🔰escape sequence
          • template literal
            • tagged template
            • tag function
          • 🔰String methods
            • str.map()
            • 💾str.capitalize()
            • str.countLetters()
            • 💾str.htmlToElement()
            • str.kebabToCamelCase()
            • 📘str.match()
              • ✨split into course names
            • 📘str.matchAll()
              • ✨matchAll() using named groups
              • ✨words in sentence
            • str.random()
            • str.removeWhitespaces()
            • 📘str.replace()
              • 🔰replacer function
              • ✨minus one
            • 💾str.replaceWhitespaces()
            • str.reverse()
            • str.shuffle()
            • 💾str.slice2()
            • 💾str.splice()
            • str.escapeHTML()
            • str.toDatasetPropName()
            • 💾str.words()
            • str.wordCounts()
            • String.fromCharCodes()
          • 👔String extension
            • 💾str.isKeyword
            • 💾str.isReservedWord
            • 💾str.isPunctuator
          • 🔰Unicode
            • 🔰code point
            • 🔰grapheme cluster
            • 🔰code plane
            • 🔰character encoding
              • 🔰UTF-16
                • 🔰code unit
                • 🔰surrogate pair
                • 🔰scalar
        • 🔺Symbol
          • 🔺Symbol.iterator
        • 🔰primitive wrapper
      • 🍄object
        • 🔸property
          • 🚩attribute
            • 🍄property descriptor
            • 💾ownPropertyFlags(obj)
          • 🔸getter/setter
          • 🔸property name
            • ⭐computed property name
          • ⭐shorthand property
          • 🔸internal property
          • 🔰property access
            • 📦property access expression
            • ➕dot notation (.)
              • ⚖️dot notation vs. decimal point
            • ➕bracket notation []
            • ➕optional chaining (?., ?.[])
              • ❓is Optional Chaining Supported?
              • 💾obj.prop(path)
            • ➕optional invocation ?.()
              • is Optional Chaining Supported?
              • obj.prop(path)
            • 🌟chaining rules
              • ✨jane.boyfriend?.money.more
          • 🔰property creation/deletion
            • 📘Object.defineProperty()
            • ➕delete
              • ❗unqualified identifier for delete
          • 🔰property testing
          • 🔰property enumeration
        • 🔰creating objects
          • ✡️object literal
            • ❗object literal is not a block❗️
          • ➕new
            • 🔸new.target
            • ⚖️callable vs. constructable
          • Ⓜ️Object.create()
            • ✨"pure" object
            • ✨guard against accidental modifications
        • 🔰extending objects
          • 📘Object.assign()
            • ⛔️ Object.assign causing TypeError
            • ❗️Object.assign copies with getter/setter
          • inheritance
            • 🔸__proto__
            • class A vs. class A extends Object
          • mixin
            • 💾mergeWithoutOverride()
            • 💾.assignDescriptors()
            • mixin inheritance
            • ✨mixin: HandleEvents
          • 💾 add default properties
          • extending built-in classes
        • 🔰converting objects
          • 🔰serializing objects
          • 🌟object -> primitive conversion
            • 🔘prefer-string
            • 🔘prefer-number
            • 🔘no preference
          • Ⓜ️.toString()
          • Ⓜ️.valueOf()
          • 💾valueToString()
        • Ⓜ️method
          • 💠[[HomeObject]]
          • 🔰polymorphism
        • 🔸prototype
          • 🔰prototype chain
            • 🚧printPrototypeChain()
      • 🍄function
        • 🔰creating functions
        • 🈯function declaration
          • ❗function in block (FiB)
          • ❗function redeclaration
          • 📘function declaration instantiation
        • 🔰function expression
          • ❗read-only identifier
          • 🔰named function expression
          • 🔰as variable
          • 🔰IIFE
        • 🔰arrow function
          • ❗arrow function as method❗️
          • 🔰arrow function as argument
          • ❗arrow function as class field❗️
          • ❗arrow function returning object literal needs (...)❗️
          • 🔰arrow function expression
        • 🔰closure
          • ❗scopes matter with closures
          • 💡private property by closure
          • ✨closure examples
            • ✨closure: to close over or not
            • ✨closure: manage grades
            • ✨closure as object
        • 🌿sorts of function
          • 🔰callback
          • 🔰higher-order function
            • 💾not(f)
            • 💾pipe(f, g, ...)
            • 💾partial(f, a, b ...)
            • 🧚decorator
              • 💾saveCallHistory(f)
              • 💾delay(f, s)
              • 💾benchmark(f)
              • 💾debounce(f, s)
              • 💾throttle(f, s)
              • ⚖️debounce vs. throttle
              • 🧚memoization
                • 🧚memoize by decorator
                  • 💾memoize(f)
                • 🧚memoize by closure
          • 🔰predicate
          • 🔰nested function
          • 🔰recursive function
            • ✨findSolution()
            • 💾deepEqual()
        • 🔰function boundary
        • 🔰function overloading
        • 🔰function forwarding
        • 🔸return value
        • 🔸function name
          • 🔰anonymous function
        • ⚖️parameter vs. argument
        • 🔸argument
          • ❗arguments are passed by value❗️
        • 🔸parameter
          • 🔸default parameter
          • 🔸rest parameters
            • 🔸rest parameters as object
          • 🔸destructured parameter
          • ⚖️simple vs. non-simple parameter
          • ❗deplicate parameters
        • 🔸prototype
        • 🔹constructor
      • 🍄class
        • 🔰initialization
          • 🔹static block
        • 🔸member
          • 🔸class field
          • 🔸private member
          • 🔸static member
            • 🔰init static property
          • private/protected members
          • 🔸getter/setter
          • 🔸method
          • 🔸bound method
        • 🔰creating class
          • 🔰delegation
        • 🔰inheritance
          • 📘super
          • derived class init
          • override constructor
          • override methods
          • override class fields
        • ✨examples
          • ✨Complex
          • ✨TypedMap
          • ✨Bag
      • 📘built-in objects
        • 📘Object
          • 🔸Object.prototype
          • 💾Object extension
            • 💾obj.mergeWith()
        • 📘Function
        • 📘Array
          • 🔸element
            • 🔸element index
              • ❗element index is string❗️
          • 🔸array length
          • 🔸array object property
          • 🍄sparse array
            • 🔰how array methods deal with "holes" ?
          • 🔰array-like
            • 👔Object+arrayLike
          • 🔰creating arrays
            • ✡️array literal
              • 🔰undefined element
          • 🔰accessing elements
          • 🔰iterating elements
          • 📘array methods
            • 📘arr.entries
            • 📘arr.filter()
            • 📘arr.flatMap()
            • 📘arr.reduce()
              • early break reduce()
            • 📘arr.slice()
            • 📘arr.splice()
          • 👔Array extension
            • 💾arr.containsSubarray()
            • 💾arr.copy()
            • 💾arr.isEmpty
            • 💾arr.last
            • 💾arr.none()
            • 💾arr.reversed()
            • 💾arr.shuffle()
            • 💾arr.randomElement
            • 💾arr.removeDuplicates()
            • 💾arr.uniqueMerge()
            • 💾arr.removeUndefined()
            • 💾arr.removeValue()
            • 💾arr.sum(), .average()
            • 💾arr.topN()
            • 💾arr.rank()
            • 💾arr.padEndSpaces()
            • 💾arr.max()
            • 💾arr.isMatrix
            • 💾arr.indexDictionaryForValues()
            • Array extensions (archive)
          • ✨array examples
            • 💾objects to HTML table
          • array & matrix methods
          • static methods
            • 📍Array(n) vs. Array(n).fill()
            • Array.from()
            • 🚧Array.list(n)
          • matrix methods
            • mat.matrixMap()
            • mat.transpose()
            • mat.maxElementInEachColumn()
          • Matrix methods
            • 🌀 Array.matrixFill()
        • 📘Set
          • 👔Set extension
            • 💾set.isEqualTo()
        • 📘Map
          • Don't use object properties
        • 📘RegExp
          • 🚩regex flag
            • 🚩flag /u
            • 🚩flag /d
          • 🔸lastIndex
          • 🔰creating regex
          • 🔰using regex
          • 🔰pattern
            • 🔰special characters
            • 🔰anchor
            • 🔰repeat
              • 🔰greedy vs. lazy
            • 🔰character set
            • 🔰alternation (or)
            • 🔰group
              • 🔰capturing group
                • 🔰named group
            • 🔰lookaround
            • 🔰inline modifier
            • 🔰replace pattern
            • 😀emoji
          • ✨regex examples
            • ✨parse .ini file
            • ✨quick brown fox
            • ✨JS-style numbers
        • 📘Proxy
          • 🔹handler method
          • 📘Reflect
          • ✨Proxy examples
            • ✨identity (proxy)
            • ✨read-only proxy
            • ✨observe()
        • 📘Date
    • 🔰variable
      • 🈯variable declaration
        • 📜var
          • ✅stop using var❗️
          • ❗var is a statement❗️
          • ❗var has no block scope❗️
          • ❗accessing var before declaration gets undefined❗️
          • ❗global var / function is global object property❗️
          • ❗var redeclaration applied even in strict mode❗️
          • ❗var in block can't shadow outer let❗️
          • ❗var can shadow parameter even in strict mode❗️
        • 🈯const
          • ⛔const requires initialization
          • ⛔const can't reassign
          • ⛔no const for classic "for"
        • 🈯let
          • ✅let redeclaration not allowed even in sloppy mode❗️
          • ✅let can't shadow parameter even in sloppy mode❗️
        • 🔰initial value
      • ❗variable redeclaration
        • ❗var redeclaration
        • ❗let redeclaration
      • 🔴accessing variables
        • ⛔temporal dead zone
          • ⛔TDZ
          • ⛔typeof let/const/class in TDZ gets an error❗️
        • ⛔uninitialized variable
        • 🔰LHS reference
        • 🔰RHS reference
        • ⚖️LHS vs. RHS reference
      • ❗variable shadowing
      • ❗can't delete variable/function❗️
    • 🔰module
      • ⭐ES module
        • 🈯import
          • ✨import ES module
          • dynamic import()
        • 🈯export
          • 📘default export
          • 📘named export
      • 🔰CommonJS
        • 📜package.json
      • ⚖️ES vs CommonJS
      • 🔰import.meta
      • 🔰package
      • 🔰bundler
      • 🔰minifier
      • 🔰browser-specific features
      • 🔰module pattern
    • 🔰iteration
      • 👔Iterable+ext
        • 💾range()
        • 💾obj.isIterable
      • 🔰iterable
        • 🔸make-iterator method
        • ❗only-iterate-once iterable
        • 🔰make iterables
        • ✨iterable examples
          • ✨ClosedRange
          • ✨Sequence
      • 🔰iterator
        • 🔸next()
        • 🔸return()
        • ❗iterators only iterate once❗️
        • 🔰iterable iterator
        • 🔰infinite iterator
        • 🔰make iterator iterable
        • 💾IteratorPrototype
        • 💾Iterator
      • 🔰iteration result
      • 📘Generator
        • 🔰generator function
          • 📘function*
          • 📦yield
          • 📘yield*
          • 🔰composition
          • 🔰generator function as ...
        • ✨generator examples
          • 💾*list()
          • 💾*integers()
          • 💾*closedRange()
          • 💾*fibonacci()
          • 💾*zip()
          • 💾*sequence()
          • 💾*interleave()
    • 🔰async code
      • 🔰thread
      • 🔰event loop
      • 🍄Promise
        • 👔custom promises
          • 💾getJSON()
          • 💾Promise.wait()
          • 💾futureValue()
        • 🔰using Promises
        • 🔰error handling
          • ⭐finally()
          • 🔰recoverable errors
        • 🔰chaining Promises
          • 💾Promise.inSeries()
        • 🔰Promises in parallel
      • 🆔async
        • 🔰async function
        • 🔰async method
        • 🔰async arrow function
        • 🔰async IIFE
        • 💾measureTime()
      • ➕await
        • 🔰await promises
          • 🔰await sequentially
          • 🔰await in parallel
        • 🔰await "thenable"
        • 🔰error handling
    • 🔰debugging
      • 🔰testing
      • 💊error handling
        • ⤴️throw
        • 🔰rethrow
        • ⤴️try-catch-finally
      • 🛠️devtools
        • 📜debugger
        • 🔰show built-in shadow DOM
    • ⛔Error
      • 🔴compile-time error
        • ⛔️ early errors
      • 🔴runtime error
      • ⛔️ SyntaxError
        • ⛔duplicate parameter not allowed in strict mode❗️
        • ⛔unexpected token "xxx"❗️
        • ⛔Named export 'xxx' not found❗️
        • ⛔unexpected number❗️
        • ⛔... parenthesis must be used to disambiguate operator precedence❗️
        • ⛔identifier 'xxx' has already been declared❗️
        • ⛔missing initializer in const declaration❗️
        • ⛔lexical declaration cannot appear in a single-statement context❗️
        • ⛔delete of an unqualified identifier in strict mode❗️
        • ⛔octal literals not allowed in strict mode❗️
      • ⛔️ ReferenceError
        • ⛔cannot access 'xxx' before initialization❗️
        • ⛔'xxx' is not defined❗️
      • ⛔️ TypeError
        • ⛔'xxx' is not a function❗️
        • ⛔assignment to constant variable❗️
        • ⛔cannot read properties of nullish❗️
        • ⛔cannot convert BigInt to Number❗️
        • ⛔cannot convert Symbol to Number❗️
        • ⛔cannot assign to read only property 'prototype' of function 'xxx'❗️
    • 🏛️Libraries
      • 🏛️JSXGraph
      • 🏛️grapheme-splitter
    • 🛠️tools
      • transpilers
    • ✨examples
      • Code Wars
        • Next Bigger Number
        • Changing Money
        • Game of Life
      • Exercises
        • flattenObject()
        • Pascal's Triangle
        • Permuations
        • Spiral Matrix
    • 💼projects
      • 💼Dictionary App
      • 💼Language: Egg
      • Calculator ❤️
      • Synth Keyboard ❤️
      • Form Validation
      • Password Generator
  • 🔰web component
    • 🔸custom element
      • lifecycle methods
      • getter/setter for attributes
      • Element Types
      • Element Info
      • Observed Attributes
      • Element Upgrades
      • Unknown Elements vs Undefined Custom Elements
      • Element-defined Content
    • 🔸shadow DOM
      • light, shadow, flattened DOM
      • shadow root vs. host
      • shadow DOM styles
        • Use CSS Variables
        • CSS reset in Shadow DOM
      • shadow DOM events
        • retargeting
        • Active Element
        • Events that Cross Shadow DOM Boundary
        • Bubbling in Shadow DOM
      • shadow DOM slots
    • 🔸<template>
      • 🔸<slot>
        • 🔸named slot
        • 🔸fallback content
        • 🔰slotted node styles
        • 🔸slotted nodes
        • 🔹`slotchange` event
        • 🔰Slot API
    • 🔸light DOM
    • 🔰implementing components
    • 🔰using components
      • 💡hide components until defined
    • Built-in Components
      • <details>
    • ✨web component examples
      • ✨<search-box>
      • <my-log> ❤️
      • <element-details>
      • <custom-dialog>
      • <user-card>
      • <expanding-list>
      • <dom-hierarchy> ⭐️
      • <time-formatted>
      • <live-timer>
      • <error-message>
      • <popup-info>
      • <custom-menu>
  • 🌐browser
    • 📘web API
      • 📘clearTimeout()
      • 📘setTimeout()
      • 🍄URL
      • 🍄navigator
      • 🔰web worker
        • 🍄self
    • 🔰concepts
      • 🔰coordinates
    • 🔰Event
      • 🔸event.type
      • 🔸event.currentTarget
      • 🔸event.target
        • ✨buttons A, B, C
      • 🔰event dispatching
      • 🔰event propagation
        • 🔰bubbling phase
        • 🔰capturing phase
        • 🔰stop propagation
          • ✨button in clickable paragraph
      • 🔰event handler
        • invocation context
        • ❌return value
        • 🔰"this" in event handler
        • 🔰register handler
          • 🔰handler options
          • capturing handler
          • 🍄object handler
        • 🔰remove handler
        • ❗window-reflecting body element event handlers
      • 🔰default action
        • ✨hide or not to hide?
        • ✨link going nowhere
      • 🌿event types
        • 🔰key event
          • ✨press shift + space
        • 🔰input event
        • 🔰mouse event
          • 🔸.buttons
          • 🔸.button
          • 🔰"click"
            • ✨draw dots
            • ✨sliding menu
            • ✨closing button [x]
            • ✨click to slide (carousel)
            • ✨click to move ball
          • 🔰"dblclick"
          • 🔰"mousemove"
            • ✨drag the bar
          • 🔰drag event
          • 👔MouseEvent+ext
          • ✨mouse event examples
            • ✨mouse event coords
        • 🔰touch event
          • 🔸.touches
          • 🔸.targetTouches
          • 👔TouchList+ext
        • 🔰scroll event
          • ✨scroll me
          • ✨scroll progress
          • ✨scroll progress on body
        • state-change
        • CSS events
      • client-side JavaScript timeline
      • custom events
        • event.isTrusted
      • "change" event
    • 🔰DOM
      • 🔰DOM hierarchy
      • 🔰querying elements
        • 💾$(), $all()
      • 🔰traversing DOM
      • 🔰create/insert/delete nodes
        • ✨table of contents
      • 🌿DOM types
        • 📘Node
          • 🔰.tagName vs .nodeName
          • 👔Node+ext
            • 💾node.isInPage
            • 🚧node.traverse()
            • 💾node.$(), .$all()
            • 🚧node.appendTag()
            • 💾newElement()
        • 📘Element
          • 🔸element content
          • 🔹.insertAdjacentHTML()
          • 🔸attribute
            • 🔸id
            • 🔰data attributes
            • ⚖️attributes vs. properties
          • 🔰box models
          • 👔Element+boxes
            • 🔸.scrollBox
              • 🔰scrollbar width
            • 🔸.paddingBox
            • 🔸.borderBox
              • 🔸.offsetParent
            • 🔸.boundingBox
              • ✨elem.showNote()
          • 👔Element+ext
            • 💾elem.isInside()
            • 💾elem.isHeading
            • 💾elem.wrappedWith()
            • 💾elem.wrappedWithHTML()
            • 🚧elem.attr()
            • 🚧elem.styleProp()
            • 🚧elem.showDataAttr()
            • 💾elem.position()
        • 🍄HTMLElement
          • 💾htmlElem.isHidden
        • 📘Document
          • 🔸document size
          • 🔸methods
            • 📘doc.elementFromPoint()
        • 📘Window
          • 🔹.requestAnimationFrame()
          • 🔰viewport
          • 🔰window scrolling
            • 🔰scroll smoothly
            • 🔰scroll lock
        • 🍄DocumentFragment
        • 🍄NodeList
          • NodeList vs. Array
        • CSSStyleDeclaration
      • ✨DOM examples
      • 👔custom methods
        • 🚧prop()
        • 🚧tag()
    • 🔰SVG
      • 💾SVGElement()
      • Attributes
        • fill
        • stroke-dasharray
        • transform
      • 🔰gradients
        • 📘<linearGradient>
        • 📘<radialGradient>
      • Patterns
      • 🔰SVG shapes
        • <rect>
        • <line>
        • <polygon>
        • <polyline>
        • <ellipse>
        • <path>
      • ✨SVG examples
        • SVG as background image
        • 3 circles
        • ✨SVG Clock
        • ✨pie chart
      • namespace
      • tools
    • 🔳<canvas>
      • 👔Canvas+ext
        • 💾drawOnCanvas2D()
        • 💾ctx.point()
        • 💾ctx.polyline()
        • 💾ctx.roundedRect()
      • 🔰rectangle
      • 🔰path
        • 🔰nonzero winding rule
        • 📘arcTo()
        • ✨curves
      • 🔰text
      • 🔰image
        • ✨canvas snapshot
      • 🔰graphics state
        • 🔰fill / stroke styles
          • 🔰colors
            • ✨color wheel
          • 🔰patterns
          • 🔰gradients
            • 💾ctx.gradient()
        • 🔰shadows
        • 🔰compositing
        • 🔰transformation
          • ✨Koch snowflake
        • 🔰clipping
      • ✨canvas examples
        • ✨square & circle
        • ✨open/closed subpaths
        • ✨fractal
        • ✨regular polygons
    • 🔰storage
    • 🔰animation
      • ✨animation examples
        • ✨moving cat
        • ✨cat in hat
        • ✨cat behind hat
  • 🔰React
    • 啟動
    • Hello React❗️
    • React Projects
      • FullStackOpen
        • Part 1
  • Server
    • HTTP status code
  • 🔖附錄
    • 👔custom
      • 👔element
        • 👔tabs control
        • 👔left tabs
      • 👔custom css
        • 👔Every Layout
      • 👔custom classes
        • 👔Vector
        • 👔Rect
        • 👔Size
        • 💾TableMaker
        • List
        • Point
        • Turtle
        • Matrix
      • 👔custom functions
        • randomElement()
        • randomColor()
        • randomPassword()
        • clone(obj)
        • functionName()
      • helper functions
    • 🧩three.js
      • 👔Director
      • 🔰renderer
        • 🔹set canvas size
      • ✨examples
        • ✨startup
      • 🔰primitives
      • 🔰scene graph
    • 🅰️Google Apps Script
      • 🍄app
        • 🔹helpers
        • 🔸app.init()
        • 🔸members
          • 🔸app.color
          • 💾app.dataRangeValuesFromSheet()
          • 💾app.sheetByName()
          • 💾app.valueOfNamedRange()
          • 💾app.deleteSheetByName()
          • 🚧app.cellValueAt()
          • ⭐app.makeTable()
          • 🚧app.mergedCellValue()
          • 🚧app.parseFields()
          • 🚧app.fetch()
          • 🚧app.resizeColumns()
          • 🚧app.setBorder()
          • 🚧app.writeAwardList()
        • 🔳prototypes
          • 🔳app.sheet.prototype
            • 💾 sheet.delete()
            • 💾 sheet.rangeByRect()
            • 💾 sheet.setValues()
            • 💾sheet.appendConditionalFormatRule()
            • 🚧SheetMethods
              • sheet.values()
          • 🔳app.range.prototype
            • range.alignCenter()
            • range.cellValue()
            • range.contains()
            • 🚧range.isEqualTo()
        • 🟥app menu
      • 🍄RawData
      • 🌿Classes
        • 🍄Sheet
        • 🍄Range
          • 🔰A1 notation
            • 💾columnName()
            • 💾A1Notation()
            • 💾A1NotationToRowColumn()
          • 🔰border
          • 📘range.setNumberFormat()
          • range.setRichTextValue()
        • 🍄ConditionalFormatRule
        • 🍄RichTextValueBuilder
        • 🍄Protection
        • 🍄Ui
      • 👔custom objects
        • 👔RangeRect
          • 💾rect.cell(i, j)
          • 💾RangeRect.byData()
        • 🚧BorderStyle
        • 🚧SheetFields, SheetField
      • 💼projects
        • 💼不能補考名單
        • 💼各班平均及前三名
        • 💼國中成績一覽表
        • 💼高中獎學金名冊
      • 💡tips
        • 💡 custom prototypes
        • 💡 refresh cells
        • 💡 條件格式:自訂公式
        • 處理字串
        • 顯示中文星期幾
        • 在儲存格使用陣列
        • Count non-blank cells
        • 💡 快速調整欄位寬度
      • ❓questions
      • 📊Google Sheet
      • 🔸GAS ⟩ commands
      • ✨GAS ⟩ examples
        • examples
        • ✨split data into tabs
          • migrate sheets into files
        • ✨event: onEdit
      • 🔰GAS ⟩ Events
        • onEdit
      • Dialogs & Sidebars
    • 📖JSDoc
      • return value
      • destructured parameter
    • 📦data structure
      • 📦Queue
      • 📦Stack
      • 📦WaitingList
      • 📦Graph
        • 🔹.findPath()
        • 🔹.findShortestPath()
        • 🤖breadth-first search
        • 🤖depth-first search
        • ✨Graph examples
          • ✨Mail Robot
    • 🔰algorithm
      • 🔰recursion
        • ✨recursion count
      • 🔰dynamic programming
      • Sort
        • sort strings
        • sort by multiple keys
        • bubble sort
      • ✨problems
        • ✨find couple
        • ✨knapsack problem
        • ✨longest path
    • 🔰paradigm
      • 🔰Test Driven Development (TDD)
      • 🔰Functional Programming (FP)
    • 🔰TypeScript
      • Setup
      • ⭐️ Cheat sheet
      • Type
        • type annotation
        • type predicate
        • Primitives
        • Narrowing
        • Tuples
        • Generics
          • Array<T>
          • Generic Functions
        • Object
          • Object Types
          • Object Properties
        • Function
          • optional/default parameters
          • return type
          • function type expression
          • callable objects with properties
          • function overloads
        • Union
          • discriminated union
      • TS Operators
    • 💎resource
      • ✏️editor
        • ✏️VSCode
          • emmet
          • shortcuts
        • ✏️replit.com
          • 🔰import .js file
      • 🔧tools
        • Vectornator
          • Tutorials
      • 📚books
Powered by GitBook
On this page

Was this helpful?

  1. 附錄
  2. data structure

Graph

PreviousWaitingListNext.findPath()

Last updated 2 years ago

Was this helpful?

⟩ Graph

  • .findPath()

  • .findShortestPath()

  • Queue - used in .

  • Stack - used in .

  • WaitingList - a Stack or Queue, used to unify breadth/depth-first search.

  • Mail Robot

  • npm ⟩ - Dijkstra's single-source shortest-paths algorithm.

implementations

  • replit >

const { log } = console;
const { Queue } = require('./Queue.js');
const { WaitingList } = require('./WaitingList.js');

// ⭐ Graph
//    2022.12.29 - 13:44 - first version
//    ❗: mutating
// -----------------------------------------------------------------
// 🟦 Graph.edgesToAdjacencyList()
// -----------------------------------------------------------------
// 🔸 .heads                 - "start" nodes of edges
// 🔸 ,tails                 - "end" nodes of edges
// 🔸 .nodes                 - all nodes
// 🔹 .neighborsOf()         - neighbors of a node
// 🔹 .addEdge() ❗          - (mutating)
// 🔹 .areNeighbors()        - check if two nodes are neighbors
// -----------------------------------------------------------------
// 🔹 .findShortestPath()    - shortest path between twe nodes
// 🔹 .distance()
// -----------------------------------------------------------------
// 🔹 .toString()
// 🔹 .findPath()            - can choose to use breadth-first or depth-first search
//
class Graph {

    #adjacencyList;

    // 🔸 store edges (adjacency list)
    //
    //    { 
    //        A: { B: 1, C: 1 }, 
    //        B: { A: 1, C: 1 },
    //    }
    //
    #edges = Object.create(null);

    // ------------
    //   tools
    // ------------

    // 🟦 Graph.edgesToAdjacencyList()
    // [[ 'A', 'B' ], [ 'A', 'C' ]] -> { A: ['B', 'C'], B: ['A'], C: ['A'] }
    static edgesToAdjacencyList(edges) {

        // adjacency list
        const list = Object.create(null);

        edges.forEach(([start, end]) => {
            if (!list[start]) list[start] = [];
            if (!list[end]) list[end] = [];
            if (!list[start].includes(end)) list[start].push(end);
            if (!list[end].includes(start)) list[end].push(start);
        });

        return list;
    }

    // ⭐ constructor
    //
    //     new Graph({ A: ['B','C','P'], B: ['A','T'] })
    //
    constructor(adjacencyList) {
        this.#adjacencyList = adjacencyList;
        for (const [start, ends] of Object.entries(adjacencyList)) {
            ends.forEach(end => {
                this.addEdge(start, end);
                this.addEdge(end, start);
            });
        }
    }

    // ------------
    //   nodes
    // ------------

    // 🔸 .heads
    get heads() {
        return new Set(Object.keys(this.#edges));
    }

    // 🔸 tails
    get tails() {
        return new Set(Object.values(this.#edges).flatMap(obj => Object.keys(obj)));
    }

    // 🔸 .nodes
    get nodes() {
        return new Set([...this.heads, ...this.tails]);
    }

    // 🔹 .neighborsOf()
    neighborsOf(start) {
        return Object.keys(this.#edges?.[start] ?? {});
    }

    // ------------
    //   edges
    // ------------

    // 🔹 .addEdge()
    addEdge(start, end) {
        if (!this.#edges[start]) this.#edges[start] = Object.create(null);
        this.#edges[start][end] = 1;    // default weight of edge
    }

    // 🔹 .areNeighbors()
    areNeighbors(start, end) {
        return (this.#edges?.[start]?.[end]) ? true : false;
    }

    // ---------------------
    //   search algorithms
    // ---------------------
    
    // 🔹 .findShortestPath()
    findShortestPath(start, target, { debug = false }={}) {
        
        const queue = new Queue();        // (queue) nodes to visit
        const path = Object.create(null); // (dict) save paths to nodes (self included)
        
        // put `start` node into queue
        queue.enqueue(start);
        path[start] = [start];            // save path to `start`
        
        // while queue is not empty
        while (!queue.isEmpty) {

            // debug
            if (debug) {
                queue.log();
                log(path);
            };
            
            // pick first node (front) from queue
            const front = queue.dequeue();
            
            // if target is found, return path.
            if (front === target) return path[front];
            
            // else add unexplored neighbors to queue
            const unexplored = this
                .neighborsOf(front)
                .filter(node => !queue.isExplored(node));
            
            for (let child of unexplored) {
                queue.enqueue(child);
                path[child] = path[front].concat(child);    // save path to child
            }
        }

        // queue is empty, no possible path.
        return null;
    }

    // 🔹 .distance()
    distance(v1, v2) {
        return (this.findShortestPath(v1, v2)?.length ?? Infinity) - 1;
    }

    // ------------
    //   debug
    // ------------

    // 🔹 .toString()
    toString() {
        return Object.entries(this.#adjacencyList)
            .map(([start, ends]) => `${start}: [${ends}]`)
            .join('\n');
    }

    // 🔹 .findPath()
    findPath(start, target, {

        // log debug info if true
        debug = false,

        // search mode:
        // • 'breadth-first': shortest (default)
        // • 'depth-first'
        mode = 'breadth-first',

    } = {}) {

        // choose stack or queue for waiting list
        const waitinglistMode = {
            'breadth-first': 'queue',
            'depth-first': 'stack',
        };

        // nodes waiting to be explored
        const waitingList = new WaitingList(waitinglistMode[mode]);

        let current = null;                   // currently examined node
        const visited = Object.create(null);  // visited nodes (a dictionary)
        const parent = Object.create(null);   // parents of each node in path

        // enqueue node
        function enqueue(node) {
            waitingList.enqueue(node);        // put node in queue
            visited[node] = true;       // mark it as visited
            parent[node] = current;     // save its parent (`current`)
        }

        // initial setup
        enqueue(start);    // put `start` in queue

        // debug
        let i = 0;
        const line = '-'.repeat(20);

        // 1. while the queue is not empty
        while (!waitingList.isEmpty) {

            // debug info
            if (debug) {
                log(`[${i}] ${line}`);
                log(`current: ${current}`);
                log(waitingList.toString());
                log(`visited: ${Object.keys(visited)}`);
                i += 1;
            }

            // pick and examine first node in queue 
            current = waitingList.dequeue();

            // 1.1 if `current` is `target`, path is found.
            if (current === target) {

                // construct the path
                let node = target;        // by starting at `target`
                let path = [target];

                // and following the parents back to `start`.
                while (node = parent[node], node !== null) {
                    path.push(node);
                }

                return path.reverse();
            }

            // 1.2 `current` is not `target`, 
            //     add unvisited neighbors to `queue`
            this.neighborsOf(current)
                .filter(node => !visited[node])
                .forEach(node => enqueue(node));
        }

        // 2. `queue` empty and `target` not found, 
        //    no path from `start` to `target`.
        return null;
    }
}

// export 
module.exports = { Graph };

💈範例:

const { log } = console;

// ⭐ import
const { Graph } = require('../DataStructure/Graph.js');
// ---------------------------------------------------------------------------

function test_Graph() {

    // ⭐ roads
    const roads = [
        "Alice's House-Bob's House", "Alice's House-Cabin",
        "Alice's House-Post Office", "Bob's House-Town Hall",
        "Daria's House-Ernie's House", "Daria's House-Town Hall",
        "Ernie's House-Grete's House", "Grete's House-Farm",
        "Grete's House-Shop", "Marketplace-Farm",
        "Marketplace-Post Office", "Marketplace-Shop",
        "Marketplace-Town Hall", "Shop-Town Hall"
    ];

    // edges
    const edges = roads.map(s => s.split('-').map(s => s[0]));
    // [
    //   [ 'A', 'B' ], [ 'A', 'C' ],
    //   [ 'A', 'P' ], [ 'B', 'T' ],
    //   [ 'D', 'E' ], [ 'D', 'T' ],
    //   [ 'E', 'G' ], [ 'G', 'F' ],
    //   [ 'G', 'S' ], [ 'M', 'F' ],
    //   [ 'M', 'P' ], [ 'M', 'S' ],
    //   [ 'M', 'T' ], [ 'S', 'T' ]
    // ]

    // ⭐ nodes (dictionary)
    const nodeName = Object.create(null);

    roads
        .flatMap(s => s.split('-').map(s => [s[0], s]))
        .forEach(([char, name]) => {
            if (!nodeName[char]) nodeName[char] = name;
        });
    // {
    //   A: "Alice's House",
    //   B: "Bob's House",
    //   C: 'Cabin',
    //   P: 'Post Office',
    //   T: 'Town Hall',
    //   D: "Daria's House",
    //   E: "Ernie's House",
    //   G: "Grete's House",
    //   F: 'Farm',
    //   S: 'Shop',
    //   M: 'Marketplace'
    // }

    // ⭐ adjacency list
    const list = Graph.edgesToAdjacencyList(edges);
    // {
    //   A: [ 'B', 'C', 'P' ],
    //   B: [ 'A', 'T' ],
    //   C: [ 'A' ],
    //   P: [ 'A', 'M' ],
    //   T: [ 'B', 'D', 'M', 'S' ],
    //   D: [ 'E', 'T' ],
    //   E: [ 'D', 'G' ],
    //   G: [ 'E', 'F', 'S' ],
    //   F: [ 'G', 'M' ],
    //   S: [ 'G', 'M', 'T' ],
    //   M: [ 'F', 'P', 'S', 'T' ]
    // }

    // ⭐ graph
    const graph = new Graph(list);

    ;[

        // graph
        // -----------------
        `graph.toString()`,
        // A: [B,C,P]
        // B: [A,T]
        // C: [A]
        // P: [A,M]
        // T: [B,D,M,S]
        // M: [P,T,F,S]
        // D: [T,E]
        // S: [T,G,M]
        // E: [D,G]
        // G: [E,F,S]
        // F: [G,M]

        `graph.areNeighbors('A', 'B')`,    // true
        `graph.areNeighbors('A', 'D')`,    // false

        `graph.nodes`,
        // Set(11) { 'A', 'B', 'C', 'P', 'T', 'D', 'E', 'G', 'F', 'S', 'M' }

        // graph.findPath('M', 'E', { debug: true, mode: 'depth-first' }),
        // graph.findPath('M', 'E', { debug: false }),
        // breadth-first: [ 'M', 'F', 'G', 'E' ]
        // depth-first  : [ 'M', 'S', 'G', 'E' ]

        `graph.findShortestPath('M', 'A', {debug: false})`,    // [ 'M', 'P', 'A' ]
        `graph.findShortestPath('G', 'C')`,                    // [ 'G', 'F', 'M', 'P', 'A', 'C' ]
        `graph.findShortestPath('M', 'X')`,                    // null

        `graph.distance('M', 'E')`,    // 3
        `graph.distance('G', 'A')`,    // 4
        `graph.distance('M', 'X')`,    // Infinity
        `graph.distance('X', 'M')`,    // Infinity

    ].forEach(cmd => {
        const value = eval(cmd);
        log(value);
    });

}
// swift

replit ⟩

🔖
📦
📦
data structure
breadth-first search
depth-first search
Ch. 7 Project: A Robot
A Guide to the Graph Data Structure
Your One-Stop Solution For Graphs In Data Structures
GRAPH TRAVERSAL (DFS/BFS)
dijkstrajs
Graph (js)
Graph (Swift)