completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Functions & closures inside the engine

Learn how V8 represents functions with SharedFunctionInfo, JSFunction objects, Contexts, feedback cells, bound functions, and arrows.

By the end, you can
  • 01
    Tell a function object from its shared codeExplain why each run of a function literal makes a new JSFunction, while all of them share one SharedFunctionInfo that holds the code.
  • 02
    Say what a closure keeps in memoryExplain how outside variables go into a Context, how sibling functions share one, and how that can keep a large value in memory.
  • 03
    Spot special kinds of functionsDescribe feedback cells, the wrapper that bind makes, and what makes arrow functions different. Keep V8's own names apart from what JavaScript promises.

What a function looks like inside the engine

The engine is the program that runs your JavaScript. Chrome and Node use an engine called V8. This lesson uses V8 version 12.4, the one that comes with Node 22.

You may already know what a closure is: a function that can still use variables from the place where it was made. This lesson opens up V8 and shows the objects it builds to make functions and closures work.

Keep two things apart the whole way:

  • What JavaScript promises. Every engine must behave this way.
  • How V8 keeps that promise. These are V8's own objects and names. Other engines use different ones.
The main idea

Every function you can call is an object. V8 calls that object a JSFunction. It points to two other things:

  • The function's code and facts about it, like its name. V8 calls this the SharedFunctionInfo. Many functions can share one.
  • For a closure, a small storage object for the outside variables it uses. V8 calls this a Context.

These lessons explain the JavaScript side first: Closures, Function objects, call, apply, and bind, and Arrow functions. Scope analysis explains how V8 decides which variables need a context. This lesson builds on them.

Real-life analogyA family recipe book everyone reads

A family keeps one recipe book. Each person reads the same recipe and cooks their own meal. Each cook picks their own amount of rice. The recipe is shared. The amount belongs to one cook.

In real life: One recipe that everyone reads
In JavaScript: The SharedFunctionInfo: the code and facts about it
In real life: Each cook making one meal from it
In JavaScript: A JSFunction: one function object
In real life: Each cook's own amount of rice
In JavaScript: A Context: the outside variables for one call
In real life: Notes on which recipes get made a lot
In JavaScript: Feedback: notes V8 keeps to make busy code faster

The table lists the four parts you'll meet. Read each row as: "this part, how many there are, and what it holds."

The four parts of a function inside V8
PartHow many are there?What does it hold?
SharedFunctionInfoOne for each function literal in your code. Every function made from that literal shares it.The code and facts about it: the name, the number of parameters, the bytecode, and where it sits in the file.
JSFunctionA new one each time a function literal runs.The function itself: what your code calls, compares with ===, passes around, or keeps in a variable.
ContextOne for each call that has outside variables a closure still needs.The outside variables a closure uses, like seed, count, or a large array.
FeedbackCell and FeedbackVectorMade as the code runs. Sometimes many functions share them.V8's notes on how the code runs: which function a call reached, what shape of object it read, and how often it ran.

Previous in this module: Fast arrays & elements kinds. Next in this module: Prototypes inside the engine.

A new function object each time

A function literal is the piece of code that makes a function, like function inner() { } or () => 1. Each time JavaScript runs that piece of code, V8 makes a brand-new JSFunction.

So two functions can run the same code and still be two different objects. === on them gives false.

The step-through below is a small model written in plain JavaScript. It acts out what V8 does. It is not V8 itself.

Step through one function literal making three functions
Step 0 of 11Ready
Your turn: follow the blue line

Step through a model of one function literal running three times. The shared code is made once. Each run makes a new function object and a new context.

Running in
  1. script
Next: line 1
let nextFunctionId = 1;let nextContextId = 1; function evaluateFunctionLiteral(seed) {  const context = { id: "Context#" + nextContextId, seed };  nextContextId += 1;  const jsFunction = {    id: "JSFunction#" + nextFunctionId,    shared: sharedFunctionInfo,    context,  };  nextFunctionId += 1;  return jsFunction;} const first = evaluateFunctionLiteral(10);const second = evaluateFunctionLiteral(20);const third = evaluateFunctionLiteral(30);console.log(first.shared === second.shared);console.log(first.id !== second.id);console.log(first.context.id !== second.context.id);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Here is what the model does, line by line:

  1. Line 1 makes one shared object for the code. It stands for the SharedFunctionInfo.
  2. Lines 17 to 19 call evaluateFunctionLiteral three times, like running a function literal three times.
  3. Each call makes a new context on line 6 and a new function object on line 8.
  4. Line 20 prints true: all three function objects point to the same shared code.
  5. Line 21 prints true: the function objects themselves are different.
  6. Line 22 prints true: each function object has its own context.
A model first, then real V8

The step-through is a model, so you can see the idea. The next section checks the same idea in real V8. The lesson's tests run that check in Node and compare which objects are the same and which are different.

SharedFunctionInfo: the shared recipe

The SharedFunctionInfo holds everything about a function that is the same for every copy. V8's own design notes list:

  • the bytecode: the short instructions V8 makes from your code and then runs;
  • the scope info: the names of the function's variables and where each one is kept;
  • where the function sits in your source file;
  • facts like its name and how many parameters it has.

None of this depends on which outer call made the function. That is why many function objects can share one.

To look inside V8, Node has a special command, %DebugPrint(value). It prints what V8 holds for that value. It only works when you start Node with the --allow-natives-syntax setting, so it never runs in a browser or in normal code.

Look inside two functions made by one function literal (Node only)JavaScript
function makeClosure(seed) {  return function inner(extra) {    return seed + extra;  };} const first = makeClosure(1);const second = makeClosure(2);%DebugPrint(first);%DebugPrint(second);

Lines 7 and 8 call makeClosure twice, so the function literal on line 2 runs twice. Lines 9 and 10 print what V8 holds for each result.

first and second are two different function objects. Both point to one shared card, the code for inner. first also points to its own context box holding seed: 1, and second to its own context box holding seed: 2.inner codeshared codefirstsecondseed: 1contextseed: 2context
first and second are two objects, but they point to one shared copy of the code. Each one also points to its own context, which holds its own seed.

The printout gives each object an address: a number that says where the object sits in memory. Two equal addresses mean one object. Two different addresses mean two objects. The printout shows these lines:

  • DebugPrint: is the address of the function object itself.
  • shared_info is the address of the shared recipe, the SharedFunctionInfo.
  • context is the address of the storage for this one closure's outside variables.
  • formal_parameter_count (the number of parameters), kind, and source code come from the shared recipe.
Checked in real Node

The lesson's tests run this code in Node 22. They check that the two function objects have different addresses, and that their shared_info addresses are the same. Addresses change on every run, so the tests compare them instead of checking exact numbers.

So "the function" can mean two things. Your code holds the JSFunction. The code and facts behind it live in the SharedFunctionInfo, which many functions share.

Contexts hold the outside variables

When a function is called, its local variables usually live in a scratch area for that one call. The engine calls it a stack frame. When the call ends, the stack frame is thrown away.

But an inner function may still need an outer variable after the outer call has ended. V8 can't keep that variable in the stack frame. So it puts it in a Context: a small object in long-lived memory (the heap) that stays as long as something points to it.

Before the code runs, V8 reads it and decides which variables need a context. When the outer function is called, V8 makes the context. For a function call, V8 names it a FunctionContext.

Step through two siblings sharing one context
Step 0 of 7Ready
Your turn: follow the blue line

Watch two functions made in one call share one context. This step-through is a model. The Node check below shows the same thing in real V8.

Running in
  1. script
Next: line 8
function outer() {  const big = ["notes", "chart", "photo"];  const readBig = () => big.length;  const answer = () => 1;  return { readBig, answer };} console.log(closures.answer());console.log(closures.readBig());
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Line 2 stores the array big. Line 3 makes readBig, which reads big. Line 4 makes answer, which does not read big. Two functions made in the same outer call are called siblings.

readBig and answer are two functions made in one call to outer. Both point to the same context box, which holds big. readBig reads big; answer never reads it.reads bigreadBiganswernever reads bigbigcontextshared by both
One call to outer() makes one context. Both functions point to it, even answer, which never reads big.

In real V8, both siblings point to the same FunctionContext. The code below checks that.

Look inside two sibling functions (Node only)JavaScript
function outer() {  const big = ["notes", "chart", "photo"];  const readBig = () => big.length;  const answer = () => 1;  return [readBig, answer];} const [readBig, answer] = outer();%DebugPrint(readBig);%DebugPrint(answer);
Checked in real Node

The tests print both functions. Their shared_info addresses are different, because they come from two different arrow functions. Their context addresses are the same, because one call to outer() made them both.

What a closure keeps alive

The engine frees memory on its own. This is called garbage collection. It frees an object only when nothing your program still uses can reach it. An object that can still be reached is reachable, and it stays in memory.

A closure keeps alive everything it can reach. In V8 that chain often looks like this:

  1. Your program holds the function object.
  2. The function object points to its context.
  3. The context holds every variable that was put in it, even ones this function never reads.

That last point matters for memory, as the next example shows.

Real-life analogyA shared notebook keeps every page

Two classmates share one notebook. One of them draws a large picture on a page. The other only needs a short note on another page. When the second classmate takes the notebook home, the drawing goes home too. It is in the same notebook.

In real life: One notebook shared by two classmates
In JavaScript: One FunctionContext shared by two sibling functions
In real life: One page holds a large drawing
In JavaScript: A large array, big, stored in the context
In real life: One classmate takes the notebook home
In JavaScript: The returned function, which never reads big
In real life: The drawing goes home too
In JavaScript: big stays in memory
Playground: which function keeps big in memory?
The code for this caseJavaScript
function outer() {  const big = new Array(1_000_000).fill("invoice");  const unused = () => big.length;  return () => 1;} const keep = outer();console.log(keep());
Contextholds big
Context of the returned functionFunctionContext shared by siblings
What it keepsbig
Does big stay in memory?yes

A sibling reads big, so big goes in one context that both siblings share. The returned function keeps that context, so big stays in memory too.

Try it yourself

Only a sibling reads big. A sibling reads big, so big goes in one context that both siblings share. The returned function keeps that context, so big stays in memory too. In this model, big stays in memory.

This is a model. It doesn't look inside your browser's engine. The Node checks below show the same thing in real V8.

The table sums up the three cases from the playground. Read each row as: "when the code looks like this, V8 does this, so this happens to big."

Which code keeps big in memory?
When the code looks like thisWhat V8 doesWhat happens to big
No inner function reads bigIt makes no context for big. The returned arrow points to the context of the whole file instead.big can be freed once the call ends.
The returned function reads bigIt puts big in a FunctionContext.big stays in memory as long as the returned function does.
Only a sibling function reads bigIt puts big in one FunctionContext that both siblings share.The returned function keeps that context, so big stays in memory too.

The next code checks this in real V8. noSibling has no inner function that reads big. withSibling has one, called unused, on line 8. Both return () => 1.

Compare the context with and without a sibling (Node only)JavaScript
function noSibling() {  const big = new Array(10);  return () => 1;} function withSibling() {  const big = new Array(10);  const unused = () => big;  return () => 1;} const noContextClosure = noSibling();const sharedContextClosure = withSibling();%DebugPrint(noContextClosure);%DebugPrint(sharedContextClosure);

In the printout, the first function points to a ScriptContext. That is the context of the whole file, and it holds nothing from noSibling. The second function points to a FunctionContext made by the call to withSibling.

The next code checks whether big really stays in memory. It uses two tools:

  • A WeakRef points to an object without keeping it alive. Its deref() method gives back the object, or undefined once the object has been freed.
  • globalThis.gc() starts garbage collection right away. It only exists when you start Node with the --expose-gc setting.
Check with a WeakRef whether big was freed (Node only)JavaScript
function forceCollection() {  for (let round = 0; round < 20; round += 1) {    globalThis.gc();    const pressure = [];    for (let i = 0; i < 50000; i += 1) pressure.push({ i });  }  globalThis.gc();} function noSibling() {  const big = { data: new Array(100000).fill(1) };  return { fn: () => 1, weak: new WeakRef(big) };} function withSibling() {  const big = { data: new Array(100000).fill(1) };  const unused = () => big;  return { fn: () => 1, weak: new WeakRef(big) };} (async () => {  const a = noSibling();  const b = withSibling();  await new Promise((resolve) => setImmediate(resolve));  forceCollection();  await new Promise((resolve) => setImmediate(resolve));  forceCollection();  console.log("no sibling alive", a.weak.deref() !== undefined);  console.log("with sibling alive", b.weak.deref() !== undefined);  console.log(a.fn(), b.fn());})();

Line 28 prints no sibling alive false: big was freed. Line 29 prints with sibling alive true: the returned function still holds the context, and the context still holds big.

Left: noSibling(). The returned function stands alone, and big lies apart from it, so big can be freed. Right: withSibling(). The returned function points to a context box that holds big, so big stays alive.noSibling()() => 1returnedbigcan be freedwithSibling()() => 1returnedbigcontextbig stays alive
Left: no inner function reads big, so nothing keeps it and it can be freed. Right: an unused sibling reads big, so big goes into a context, and the returned function keeps that context.
Checked in real Node

The tests check two facts. First, the function from noSibling points to a ScriptContext, and the function from withSibling points to a FunctionContext. Second, after garbage collection, the first WeakRef is empty, and the second one still holds big.

To hunt real memory problems, go on to Memory management and Finding memory leaks. The usual cause is not that a closure exists. It is that something long-lived, like an event listener, a timer, or a cache, still holds the closure.

Feedback cells: notes shared by many closures

While your code runs, V8 takes notes on how it runs. These notes are called feedback. For example:

  • which function a call ended up calling;
  • what shape of object a property was read from;
  • whether a + only ever saw small whole numbers;
  • how often the function has run.

V8 uses these notes to make busy code faster. It keeps them in two places:

  • A FeedbackVector is the list of notes itself.
  • A FeedbackCell sits between the function object and that list. It lets V8 make the list only when the code is used, and share it when that is safe.

The code below makes one factory that creates one function, and a second factory that creates two functions from the same literal. Then it calls each function 40 times, so V8 has something to note. Calling code many times like this is called warming it up.

Feedback cells for one function and for many (Node only)JavaScript
function oneFactory(n) {  return function addOne(x) {    return x + n;  };}const only = oneFactory(1);for (let i = 0; i < 40; i += 1) only(i);%DebugPrint(oneFactory); function manyFactory(n) {  return function addMany(x) {    return x + n;  };}const first = manyFactory(1);const second = manyFactory(2);for (let i = 0; i < 40; i += 1) {  first(i);  second(i);}%DebugPrint(manyFactory);%DebugPrint(first);%DebugPrint(second);

A factory keeps one feedback cell for the function literal inside it. The cell says whether that literal has made one function so far, or many.

Checked in real Node

The tests check three things in the printout:

  • Line 8 prints oneFactory with FeedbackCell[one closure].
  • Line 21 prints manyFactory with FeedbackCell[many closures].
  • Lines 22 and 23 print first and second. They have the same feedback vector address but different context addresses.

You don't need to change your code because of this. It shows that V8 can share code, and sometimes notes, while still giving each closure its own variables. The type feedback and inline caches lessons explain the notes in more depth.

One per function, or shared?
  • The function that each call to makeClosure() returns.
  • The inner function's name, bytecode, number of parameters, and variable names.
  • The seed value from one call to the factory.
  • A large array, big, that only a sibling function reads.
  • V8's notes on x + n, after two functions from one literal both ran 40 times.
  • bound_target_function, bound_this, and bound_arguments.
  • An arrow's kind: ArrowFunction and its missing prototype.
Try it yourself
0 of 7 correct

Put each fact where it belongs: one per function object, in the shared code and notes, in a context, or in a special kind of function. Each card explains why once you place it.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Bound functions and arrow functions inside V8

fn.bind(thisValue, ...args) makes a new function that always calls fn with that this and those first arguments. It does not change fn. It makes a wrapper: a new function object that stores three things and then calls the original. V8 calls this wrapper a JSBoundFunction. It stores:

  • the function to call, as bound_target_function;
  • the this value to use, as bound_this;
  • the arguments to put first, as bound_arguments.
A bound function puts its saved arguments firstPop out in the code editor (opens in a new tab)JavaScript
function total(fee, tax) {  return this.base + fee + tax;} const bill = total.bind({ base: 100 }, 20);console.log(bill(5));console.log(bill.length);console.log(bill.name);

In plain words, line by line:

  1. Line 5 makes bill. It saves { base: 100 } as this and 20 as the first argument, fee.
  2. Line 6 calls bill(5). The 5 becomes tax, so total returns 100 + 20 + 5 and it prints 125.
  3. Line 7 prints 1. A function's length is how many parameters it expects. Only tax is still open.
  4. Line 8 prints bound total. A bound function's name starts with bound.
Calling bill(5). The argument 5 goes into a cart called bill. The cart holds the function total, the object with base 100, and the stored argument 20. Out comes 125.5you passtotal{base:100}20bill125bill(5) gives
bill is a new wrapper. It stores total, the this object, and the first argument 20. Calling bill(5) runs total with all of them, so it gives 100 + 20 + 5 = 125.
Look inside a bound function (Node only)JavaScript
function total(fee, tax) {  return this.base + fee + tax;} const bound = total.bind({ base: 10 }, 1);%DebugPrint(bound);

The printout says JSBoundFunction and shows the three saved parts. The original total is not changed.

Arrow functions are different. They are still function objects, but V8 marks their kind as ArrowFunction. An arrow function:

  • has no this of its own. It uses the this of the code around it.
  • has no arguments of its own. It uses the arguments of the function around it.
  • has no prototype property.
  • can't be called with new.
Arrows use this and arguments from the code around themPop out in the code editor (opens in a new tab)JavaScript
const shop = {  label: "Kochi counter",  makeReader() {    return () => this.label;  },}; function makeFirst() {  return () => arguments[0];} const reader = shop.makeReader();const first = makeFirst("Asha", "Ravi");console.log(reader.call({ label: "Mumbai counter" }));console.log(first());console.log("prototype" in first);try {  new first();} catch (error) {  console.log(error.name);}

In plain words, line by line:

  1. Line 4 makes an arrow inside makeReader. It uses makeReader's this, which is shop.
  2. Line 9 makes an arrow that reads arguments[0] of makeFirst.
  3. Line 14 tries to give the arrow a new this with .call(). It still prints Kochi counter.
  4. Line 15 prints Asha, the first argument passed to makeFirst on line 13.
  5. Line 16 prints false: the arrow has no prototype property.
  6. Line 18 calls the arrow with new. That throws, and line 20 prints TypeError.
Look inside an arrow function (Node only)JavaScript
const arrow = () => this;%DebugPrint(arrow);

The printout shows kind: ArrowFunction and function prototype: <no-prototype-slot>. That means V8 did not even leave room for a prototype.

The table compares the three kinds. Read each row as: "this kind of function, what V8 builds for it, and how it acts."

Normal, bound, and arrow functions
Kind of functionWhat does V8 build?How does it act?
Normal functionA function object. It usually has a prototype property and can be used with new.Its this depends on how you call it, or on bind.
Bound functionA JSBoundFunction wrapper. It stores the function to call, the this to use, and the first arguments.It puts the saved arguments before the ones you pass. It works with new only if the original function does.
Arrow functionA function object whose kind is ArrowFunction. It has no room for a prototype.It has no this or arguments of its own, and it can't be used with new.

For how these act in everyday code, see call, apply, and bind and Arrow functions. This section showed what V8 builds behind them.

Habits for memory and speed in real code

Most closures are fine, and you don't need to think about contexts every day. Use this lesson when a tool shows a real problem:

  • a heap snapshot, a DevTools list of every object in memory and what keeps it there;
  • an allocation profile, which shows where your code makes new objects;
  • a performance trace, which shows where time goes.

Then look for something long-lived that still holds a closure: an event listener, a timer, a cache, a promise that never finishes, or a top-level array of callbacks.

A cleanup function lets go of the listenerPop out in the code editor (opens in a new tab)JavaScript
function mountPanel(root, report) {  const state = { clicks: 0 };  function onClick() {    state.clicks += 1;    report(state.clicks);  }  root.addEventListener("click", onClick);  return () => root.removeEventListener("click", onClick);} const events = [];const root = {  listeners: new Set(),  addEventListener(_type, fn) { this.listeners.add(fn); },  removeEventListener(_type, fn) { this.listeners.delete(fn); },  click() { for (const fn of this.listeners) fn(); },}; const cleanup = mountPanel(root, (count) => events.push(count));root.click();root.click();cleanup();root.click();console.log(events.join(","));console.log(root.listeners.size);

In plain words, line by line:

  1. Line 2 makes a small state object.
  2. Line 3 makes onClick, a closure that uses state.
  3. Line 7 adds onClick to root's listeners. Now root holds the closure.
  4. Line 8 returns a cleanup function that removes onClick again.
  5. Lines 20 and 21 click twice. Line 22 runs the cleanup. The click on line 23 runs no listener.
  6. Line 24 prints 1,2, and line 25 prints 0 listeners.

After line 22, root no longer holds onClick. When nothing else points to it, the closure and its state can be freed.

  • Keep only what you need in a closure. If a callback only needs an ID, keep the ID, not the whole server response.
  • Remove listeners, timers, observers, and subscriptions when a part of the page goes away or a server task ends.
  • Find what keeps an object alive in a heap snapshot before you change code. Names like Context and JSFunction are clues.
  • Measure speed before you change it. Shared code and shared notes rarely matter unless a profiler points at busy code.

Common misconceptions

  • "A closure copies every variable." It doesn't copy them. It points to a context, and the context holds the variables V8 decided to put there.
  • "Every function object has its own copy of the code." Many function objects can share one SharedFunctionInfo.
  • "If my returned function never reads a value, it can't keep that value alive." A sibling that reads it can put it in a shared context. The returned function keeps that context, and so the value.
  • "bind changes the original function." It makes a new wrapper that stores the function, this, and the first arguments.
  • "V8's objects are part of JavaScript." JavaScript only promises how closures act. Names like JSFunction belong to V8, and other engines use different ones.

The table puts side by side four ideas that are easy to mix up. Read each row as: "this idea, what it is, and what it is not."

Similar ideas that are easy to mix up
IdeaWhat is it?What is it not?
Function valueThe function object you keep in a variable and call.It is not the shared code. Many function values can share one copy of the code.
ClosureA function object that can still reach the context where it was made.It is not a copy of every variable around it.
ContextA small object that holds the outside variables a closure still needs after a call ends.It is not made for every local variable. Most local variables never go in one.
FeedbackV8's notes on how code runs, kept in feedback cells and feedback vectors.It is not part of JavaScript, and it is not a type you write in your code.

Practice exercises

Exercise 1 · Warm-upAre the two functions the same object?

Read the code and type the three values it prints.

Starter codePop out in the code editor (opens in a new tab)JavaScript
function makeClosure(seed) {
  return function inner(extra) {
    return seed + extra;
  };
}
const first = makeClosure(1);
const second = makeClosure(2);
console.log(first === second);
console.log(first(10));
console.log(second(10));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeTwo siblings, one context

    What two values does this code print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    function outer() {
      const big = ["notes", "chart", "photo"];
      const readBig = () => big.length;
      const answer = () => 1;
      return { readBig, answer };
    }
    
    const closures = outer();
    console.log(closures.answer());
    console.log(closures.readBig());

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeWhat does the bound function give?

      Type the three values this code prints.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function total(fee, tax) {
        return this.base + fee + tax;
      }
      
      const bill = total.bind({ base: 100 }, 20);
      console.log(bill(5));
      console.log(bill.length);
      console.log(bill.name);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFix the function that keeps too much

        A factory makes a huge array, big. It also makes an unused sibling function that reads big. Then it returns a different function. Name one safe fix so big doesn't stay in memory.

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · PracticeWrite a small factory

          Finish the factory so it prints order:Asha. Then say which outside variable the arrow uses.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          function makeLabel(prefix) {
            // return an arrow that adds ":" and a name
          }
          
          const label = makeLabel("order");
          console.log(label("Asha"));
          Exercise 6 · ChallengeUse cleanup in a real app

          After the cleanup function runs in this code, how many listeners are left?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          function mountPanel(root, report) {
            const state = { clicks: 0 };
            function onClick() {
              state.clicks += 1;
              report(state.clicks);
            }
            root.addEventListener("click", onClick);
            return () => root.removeEventListener("click", onClick);
          }
          
          const events = [];
          const root = {
            listeners: new Set(),
            addEventListener(_type, fn) { this.listeners.add(fn); },
            removeEventListener(_type, fn) { this.listeners.delete(fn); },
            click() { for (const fn of this.listeners) fn(); },
          };
          
          const cleanup = mountPanel(root, (count) => events.push(count));
          root.click();
          root.click();
          cleanup();
          root.click();
          console.log(events.join(","));
          console.log(root.listeners.size);

          Answer, then press Check. Spacing and letter case don’t matter.

            Check your understanding

            Function internals quiz · 8 questionsScore: first tries count
            1. Question 1 of 8What does the SharedFunctionInfo hold in V8?

              Choose an answer to see the explanation.

            2. Question 2 of 8What does this code with two closures print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function make(seed) {
                return function add(x) {
                  return seed + x;
                };
              }
              const a = make(1);
              const b = make(1);
              console.log(a === b);
              console.log(a(4), b(4));

              Choose an answer to see the explanation.

            3. Question 3 of 8One call to outer() makes two sibling functions. In V8, both point to the same FunctionContext. What can go wrong?

              Choose an answer to see the explanation.

            4. Question 4 of 8What does this bound function print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function total(fee, tax) {
                return this.base + fee + tax;
              }
              const bill = total.bind({ base: 100 }, 20);
              console.log(bill(5));
              console.log(bill.length);
              console.log(bill.name);

              Choose an answer to see the explanation.

            5. Question 5 of 8What does this arrow function print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const shop = {
                label: "Kochi counter",
                makeReader() {
                  return () => this.label;
                },
              };
              const reader = shop.makeReader();
              console.log(reader.call({ label: "Mumbai counter" }));
              console.log("prototype" in reader);

              Choose an answer to see the explanation.

            6. Question 6 of 8In the lesson's feedback cell check, what changed when a factory made a second function from the same literal?

              Choose an answer to see the explanation.

            7. Question 7 of 8Which sentence keeps what JavaScript promises apart from how V8 works?

              Choose an answer to see the explanation.

            8. Question 8 of 8A memory tool shows that closures keep too much in memory. What should you do?

              Choose an answer to see the explanation.

            Key takeaways

            • Each time a function literal runs, V8 makes a new JSFunction. Many of them can share one SharedFunctionInfo, which holds the code.
            • A Context holds the outside variables a closure still needs. Siblings made in one outer call share one context.
            • A returned function can keep a large value in memory through a shared context, even when only a sibling reads that value.
            • Feedback is V8's notes on how code runs. A feedback cell records whether a literal made one function or many.
            • A bound function is a wrapper that stores the function, this, and the first arguments. An arrow has no this, arguments, or prototype of its own, and can't be used with new.

            Remember the one-liner.
            A closure is a function object that points to shared code and, when it needs one, to a context that holds its outside variables.

            Next: Prototypes inside the engine. It shows how V8 uses the same kind of notes to find a property on a parent object quickly.

            CompleteFrontend Clear concepts. Working examples.