6 Functions
Functions in JavaScript are more than syntax for reusable code: they are real objects with special internal capabilities that determine whether they can be called, constructed with new, or used to capture surrounding variables. The chapter explains that these hidden mechanics are what make functions behave so differently from ordinary objects, and why understanding them helps make sense of common bugs involving constructors, this, closures, and performance. It also shows that the different function forms are not interchangeable, because each form is created with different built-in features and restrictions.
The text breaks down how function creation and invocation actually work. It explains the four steps behind new, including object creation, prototype linkage, constructor invocation, and return-value handling, and shows how new.target can detect constructor calls. It then compares function declarations, expressions, arrows, classes, and the Function constructor, emphasizing that these forms differ in hoisting behavior, constructor support, this binding, arguments, and scope access. Arrow functions are presented as intentionally limited tools for callbacks, while classes are shown as constructor functions with extra guardrails, private fields, and non-enumerable methods.
The chapter also treats closures and engine behavior as practical concerns, not abstract ideas. Closures are described as heap-allocated context objects that can retain far more memory than expected, especially when functions are created inside loops or attached to long-lived handlers. Finally, it explains how V8 interprets, compiles, optimizes, and deoptimizes functions through lazy parsing, Ignition, Sparkplug, Maglev, and TurboFan. The central takeaway is that stable types and consistent calling patterns allow V8 to optimize aggressively, while changing shapes, argument types, or captured scope can trigger deoptimization, memory growth, and surprising slowdowns.
The V8 Compilation Pipeline
V8 will progressively optimize JavaScript code in stages based on the runtime feedback it receives while the code is executing.
Summary
- Functions are objects distinguished by internal slots: [[Call]] makes them callable, [[Construct]] enables the new keyword, and [[Environment]] powers closures.
- The new operator performs four steps: create object, link prototype, call constructor with bound this, and return the object unless the constructor returns a different one.
- Declarations, expressions, arrows, and classes create structurally different function objects with different capabilities around this binding, arguments access, and constructor use.
- For regular functions, this is determined at call time through four rules in precedence order: new binding, explicit binding, implicit binding, and default binding.
- Arrow functions capture this from their surrounding scope at creation and never reevaluate it.
- Closures allocate Context objects on the heap that persist as long as any function references them, capturing all variables in scope regardless of whether the function uses them.
- V8's compilation pipeline progresses from lazy parsing through Ignition bytecode to Sparkplug/Maglev baseline compilation and finally TurboFan optimization for hot functions.
- Type feedback vectors track argument types and object shapes, with functions classified as monomorphic, polymorphic, or megamorphic based on the consistency of their inputs.
- Deoptimization occurs when type assumptions break, causing V8 to abandon optimized machine code and fall back to slower execution paths.
JavaScript in Depth ebook for free