Overview

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.

FAQ

What makes a JavaScript function different from a regular object?Every function is an object, but it also has special capabilities. Functions inherit from Function.prototype, can have properties, and—most importantly—can be called because they have the internal slot [[Call]].
Why does new work with some functions but not with arrow functions?new only works on functions that have the internal slot [[Construct]]. Regular functions and classes have it, but arrow functions do not, so using new with an arrow function throws a TypeError.
What are the four steps performed by the new keyword?new creates a fresh object, links its [[Prototype]] to the constructor’s prototype, calls the constructor with this bound to the new object, and then returns either that object or a returned object from the constructor.
How can a function tell whether it was called with new?It can check new.target. If the function was called normally, new.target is undefined. If it was called with new, new.target refers to the constructor function itself.
What is the difference between a function declaration and a function expression?A function declaration is hoisted, so it can be called before it appears in the source. A function expression is created at runtime like any other value, so it is only available after execution reaches that line.
Why are named function expressions useful?The name of a named function expression exists only inside the function body, not in the outer scope. This gives the function a reliable self-reference, which is especially useful for recursion and for surviving reassignment of the outer variable.
Why do arrow functions behave differently with this?Arrow functions do not have their own this binding. Instead, they capture this from the surrounding scope when they are created, and that value does not change at call time.
Why can closures cause memory leaks?When a function captures outer variables, V8 stores them in a heap-allocated Context object. If a returned function or long-lived callback keeps a reference to that Context, all captured variables stay alive too—even ones the function never uses.
Why should functions created inside loops be avoided?Each function created inside a loop gets its own closure Context and its own metadata. In large loops, that can create thousands of extra heap objects and retain more memory than expected, especially if any captured variable is large.
How does V8 optimize functions over time?V8 starts with lazy parsing and bytecode execution in Ignition. Frequently called functions may be promoted to Sparkplug or Maglev, and hot, stable functions can be optimized further by TurboFan. If argument types or object shapes change, V8 may deoptimize and fall back to slower code.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • JavaScript in Depth ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • JavaScript in Depth ebook for free