You Don't Know JS Yet (2nd Edition) Documentation

repository·2nd-ed·Indexed 34 days ago

https://github.com/getify/you-dont-know-js

A deep-dive technical series exploring the core mechanics of JavaScript and ECMAScript. Topics include lexical scope, closures, variable lifecycle, prototypal classes, value vs. reference assignment, name inference, and the TC39 proposal process.

Tokens
31.8K
Snippets
61
Records
138
Agent score
99%

What's inside You Don't Know JS Yet

  1. Understand JS file execution and program boundaries

    2nd-ed

    In JavaScript, every standalone .js file is treated as its own separate program. This has implications for error handling: a failure in one file (during parsing, compilation, or execution) does not necessarily prevent subsequent files from being processed.

    Multiple files act as a single application by sharing state and functionality through the global scope.

    If using ES6 modules (import statements or <script type=module>), each module is also treated as a separate program that inter-operates with others via the module-loading mechanism.

  2. Understand the concept of Closure in JavaScript

    2nd-ed

    A closure is a behavior of functions where an inner function remembers and maintains access to variables from its enclosing lexical scope, even after the outer function has finished executing.

    Key characteristics:

    • Function-only: Only functions (and methods) exhibit closure. Objects and classes themselves do not have closure, though their methods might.
    • Instance-based: Closure is associated with a specific instance of a function created at runtime, not just the lexical definition. Every time an outer function is called and returns an inner function, a new closure is created for that specific instance.
    • Live Link: A closure is not a static snapshot of a value; it is a live link to the variable itself. You can both read from and re-assign (write to) the closed-over variables.
    function makeCounter() {
        var count = 0;
    
        return function getCurrent() {
            count = count + 1;
            return count;
        };
    }
    
    var hits = makeCounter();
    hits(); // 1
    hits(); // 2
  3. Understand Implied Function Name Scope

    2nd-ed
    When using a named function expression, the name identifier (e.g., ofTheTeacher in function ofTheTeacher(){...}) is placed in its own implied scope. This scope is nested between the outer enclosing scope and the inner function scope. Because it is in a separate scope, you can shadow the function name within the function body without causing a re-declaration error.
  4. Understand Lexical Scope, Hoisting, and the Temporal Dead Zone

    2nd-ed

    JavaScript uses lexical scope, meaning the scope structure is determined at the time the program is parsed (author-time). Key characteristics include:

    • Hoisting: All variables declared anywhere in a scope are treated as if they are declared at the beginning of that scope.
    • Function-scoped var: Variables declared with var are scoped to the nearest function, even if they appear inside a block.
    • Temporal Dead Zone (TDZ): A behavior associated with let and const declarations where variables are observable but unusable until the execution reaches their actual declaration point.
    • Closure: A natural result of lexical scope where a function maintains access to its original scope variables even when executed in different scopes.
  5. Understand the roles of Engine, Compiler, and Scope Manager

    2nd-ed

    To understand how JavaScript processes code, conceptualize the interaction between these three components:

    • Engine: The overall orchestrator responsible for the start-to-finish compilation and execution of the program.
    • Compiler: A part of the Engine that handles parsing (lexing/parsing into an AST) and code generation. It identifies declarations and instructs the Scope Manager to prepare the environment.
    • Scope Manager: A component that maintains a lookup list of all declared variables/identifiers and enforces accessibility rules based on the current scope.

    The Process Flow:

    1. Compilation Phase: The Compiler scans the code. When it finds a declaration (e.g., var x), it asks the Scope Manager to register that identifier in the current scope bucket. If it finds a function or block, it tells the Scope Manager to create a new scope bucket.
    2. Execution Phase: The Engine runs the generated code. When an assignment or reference occurs (e.g., x = 10), the Engine asks the Scope Manager to find the variable x by looking in the current scope and then traversing up the scope chain until it is found.
  6. Understand JavaScript's multi-paradigm nature

    2nd-ed

    JavaScript is a multi-paradigm language, meaning it supports various programming styles and allows you to mix them within a single application. You are not forced into a single approach and can choose a style on a line-by-line basis.

    Supported paradigms include:

    • Procedural: Organizing code in a top-down, linear progression of operations (procedures).
    • Object-Oriented (OO): Organizing logic and data into units called classes.
    • Functional (FP): Organizing code into pure functions and treating functions as values.
  7. Understand WebAssembly (WASM) vs JavaScript

    2nd-ed

    WebAssembly (WASM) is a binary-packed representation format designed to run in JS engines with minimal processing. It is not a replacement for JavaScript, but an augmentation.

    Key Differences

    • Execution: WASM uses Ahead-of-Time (AOT) compilation, skipping the standard JS parsing/compilation delays.
    • Typing: WASM relies heavily on static typing information, making it more suitable for languages like C, Go, or AssemblyScript than for highly dynamic languages like JavaScript or TypeScript.
    • Purpose: WASM provides a path for non-JS programs to run on the web and acts as a cross-platform virtual machine (VM) that can run in various system environments.
  8. Understand the JS execution lifecycle: Parsing, Compiling, and Executing

    2nd-ed

    JavaScript is a parsed language that behaves in spirit like a compiled language. Because JS is parsed before execution, the engine can identify and report "early errors" (static errors like duplicate parameter names or malformed syntax) before any code actually runs.

    The typical execution flow for a JS program is:

    1. Transpilation & Bundling: Tools like Babel and Webpack transform and pack the source code.
    2. Parsing: The JS engine parses the source code into an Abstract Syntax Tree (AST).
    3. Compilation: The engine converts the AST into a binary intermediate representation (IR) or byte code.
    4. Optimization: An optimizing JIT (Just-In-Time) compiler refines the code.
    5. Execution: The JS Virtual Machine (VM) executes the optimized code.
  9. Identify the three pillars of JavaScript

    2nd-ed

    The core mechanics of JavaScript are built upon three fundamental pillars that should be mastered to understand the language deeply:

    1. Scope and Closure: Lexical scope, how it supports closures, and how the module pattern organizes code.
    2. Prototypes: How this works, how object prototypes support delegation, and how prototypes enable the class mechanism.
    3. Types and Grammar: Types, type coercion, and how syntax and grammar define code structure.
  10. Distinguish between JavaScript and Web/Node.js APIs

    2nd-ed

    It is important to distinguish between the JavaScript language specification (ECMAScript) and the APIs provided by the environment (the host).

    • JavaScript (ECMAScript): The core language specification.
    • Web APIs: Functions like fetch(), getCurrentLocation(), and getUserMedia() are provided by the browser environment, not the JS language itself.
    • Node.js APIs: Methods like fs.write() are provided by Node.js built-in modules.
    • Consensus APIs: Methods like console.log() are not part of the official JS specification but are implemented by almost all environments for utility.

    Treating environment-specific APIs as core language features can lead to confusion when moving code between different environments (e.g., from Browser to Node.js).

  11. Understand the implementation of closure: Per-variable vs Per-scope

    2nd-ed

    There are two ways to conceptualize how closures behave in JavaScript:

    1. Per-variable (Conceptual/Optimized): Conceptually, a closure is often thought of as applying only to the specific variables explicitly referenced by the inner function. Many modern engines apply an optimization that removes unreferenced variables from the closure scope to save memory.
    2. Per-scope (Implementation/Specification): Implementation-wise, closure must be per-scope. This means the entire scope chain is preserved. If an optimization cannot be applied (for example, if eval() is used within the scope), all variables in that scope will be held in memory, even if they aren't explicitly used by the inner function.

    Best Practice: Do not rely solely on engine optimizations for memory management. If a variable holds a large value, explicitly nullify it if it is no longer required.

  12. Explore Object Delegation via Prototypes

    2nd-ed

    While many developers use the ES6 class keyword to implement class-style programming, JavaScript's core strength lies in its prototype system.

    Instead of traditional class inheritance, you can use behavior delegation. This involves allowing objects to connect and cooperate dynamically through the prototype chain and sharing a this context. This approach embraces objects as objects and can be a more powerful way to organize behavior and data than strict class hierarchies.