completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Functions are objects

Inspect JavaScript functions as callable objects: name, length, inferred names, custom properties, constructors, toString, and safe forwarding wrappers.

By the end, you can
  • 01
    Read a function's propertiesRead a function's name, length, and code as text, and check it with typeof and instanceof Function.
  • 02
    Use names and your own properties with careExplain how unnamed functions get names, what a function's inner name does, and why names can change when a site is built.
  • 03
    Write safe wrappersPass on this and every argument with apply, and explain why new Function is risky.

Functions are objects you can call

You already know functions as actions. You call them with round brackets, (), pass values in, and get a result back. This lesson shows the other side. In JavaScript, a function is also an object. An object is a value that holds named properties, like user.name.

So a function can do the same things as any other object:

  • It has properties you can read, like save.name and save.length.
  • You can add your own properties to it, like save.count = 0.
  • It has methods you can call, like save.toString() and save.bind(...).

And unlike most objects, you can still call it: save().

The function save drawn as a cart. It holds a call card with round brackets, a name card, and a length card. Reading save.name gives the string save. Reading save.length gives 1.()namelengthsavesave.name"save"save.length1
The function save is one object. You can call it with (). You can also read its properties, like any object: save.name is "save" and save.length is 1.
Real-life analogyA phone contact with a note

A phone contact does two jobs. You can tap it to make a call. You can also read its name and add a note to it. A JavaScript function is the same. You can call it, and you can read and add properties.

In real life: Tap the contact to call
In JavaScript: Call the function: save()
In real life: The contact has a name
In JavaScript: save.name is the function's name
In real life: The contact lists what it needs
In JavaScript: save.length tells how many inputs the function expects
In real life: Add a note to the contact
In JavaScript: Add your own property, like save.count

This lesson covers these topics, in this order:

  • name and length, two properties every function has.
  • How JavaScript gives a name to a function that has none.
  • Your own properties on a function.
  • Named function expressions.
  • new Function, which builds a function from text.
  • The constructors for generator and async functions.
  • toString(), which gives a function's code as text.
  • Wrappers: functions that add a little work around another function.

name and length: the name and the input count

STEP THROUGH

Every function has two built-in properties:

  • name is a string. It is the function's name, such as "save".
  • length is a number. It counts the function's parameters: the input names in its round brackets. But it only counts the parameters that come before the first one with a default value.

Two words you need for the rule:

  • A default value is written with =, like priority = "normal". It is used when the caller leaves that input out.
  • A rest parameter is written with three dots, like ...labels. It collects all the extra inputs into an array. It is never counted.
Read a function's name and lengthPop out in the code editor (opens in a new tab)JavaScript
function save(title, priority = "normal", ...labels) {  return title;}console.log(save.name, save.length);

Line 1 makes a function named save with three parameters:

  • title has no default value, so it is counted.
  • priority = "normal" has a default value, so counting stops here.
  • ...labels is a rest parameter, so it is never counted.

Line 4 prints the name and the length: save 1.

The three parameters of save in a row. title is counted. priority equals normal has a default value, so counting stops there. The rest parameter labels is not counted. So save.length is 1.the parameters of savetitlepriority = "normal"...labelscountedstops herenot countedsave.length === 1
JavaScript counts parameters from the left. It stops at the first one with a default value. A rest parameter is never counted. Only title is left, so save.length is 1.

The next example has three functions that all have a length of 0. It also uses bind. add.bind(null, 1, 2, 3) makes a new function, called a bound function. The null sets this. The numbers after it are inputs filled in ahead of time.

Three functions with length 0Pop out in the code editor (opens in a new tab)JavaScript
function defaultsFirst(value = 3, later) {}function restOnly(...items) {}function add(first, second) { return first + second; }const boundTwice = add.bind(null, 1, 2, 3);console.log(defaultsFirst.length, restOnly.length, boundTwice.length);

Line 5 prints 0 0 0. Here is why, line by line:

  1. Line 1: the first parameter, value = 3, has a default value. Counting stops before anything is counted.
  2. Line 2: ...items is a rest parameter, and rest parameters are never counted.
  3. Line 4: add has a length of 2. bind fills in three inputs, so no inputs are left. The new function's length is 0. It never goes below 0.

Step through four functions below and watch JavaScript set each one's name and length.

Read name and length from four functions
Step 0 of 4Ready
Your turn: follow the blue line

Step through four functions and watch JavaScript set each one's name and length.

Running in
  1. script
Next: line 1
  return a + b;}const inferred = function (first, second = 2, ...rest) {  return first + second + rest.length;};const arrow = (value, index) => value + index;const bound = declared.bind(null, 1);console.log(declared.name, declared.length);console.log(inferred.name, inferred.length);console.log(arrow.name, arrow.length);console.log(bound.name, bound.length);
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.

The table below shows common kinds of functions. Read each row as: "this code gives this name and this length."

The name and length of common functions
Kind of functionExampleWhat is its name?What is its length?
A function declarationfunction add(a, b) {}add2
An unnamed function saved in a constconst add = function (a, b) {}add2
A default value before other parametersfunction f(a, b = 1, c) {}f1
A rest parameterfunction f(a, ...rest) {}f1
bind with one input filled inadd.bind(null, 1)bound add1
A getterget total() {}get total0
Where length stops counting

function f(a, b = 1, c) {} has a length of 1, not 3. Counting stops at b = 1, so c is not counted either. function g(a, ...rest) {} also has a length of 1, because rest parameters are never counted.

How an unnamed function gets a name

INTERACTIVE

Many functions are written without a name, like function () {} or () => {}. These are called anonymous functions. When you save one under a name, JavaScript copies that name onto the function. This is called name inference.

For example, const greet = () => {} gives greet.name === "greet". The function had no name, so it took the variable's name.

Pick a kind of function in the inspector below. It shows the function's name and length, plus two checks: typeof gives the type of a value as text, and instanceof Function asks "is this a kind of Function?"

Function inspector
Function declarationPop out in the code editor (opens in a new tab)JavaScript
function add(a, b) { return a + b; }
What JavaScript reportsdeclaration
nameadd
length2
typeoffunction
instanceof Functiontrue
Try it yourself

Pick a kind of function. The values shown are real JavaScript results, checked by the lesson's tests. Build tools may still change names and code on a live site.

Pick a function to see its name, length, and type. The page shows saved results; it never turns text into code.

The code below shows five more places a name can come from. Lines 9 to 13 each print one name:

Five places a name can come fromPop out in the code editor (opens in a new tab)JavaScript
const assigned = function () {};const object = {  run() {},  get total() { return 42; },  [Symbol("secret")]() {},};const descriptor = Object.getOwnPropertyDescriptor(object, "total");const fallback = (fn = function () {}) => fn.name;console.log(assigned.name);console.log(object.run.name);console.log(descriptor.get.name);console.log(Object.getOwnPropertySymbols(object).map((key) => object[key].name)[0]);console.log(fallback());
  1. Line 1 saves an unnamed function in assigned. So line 9 prints assigned.
  2. Line 3 is a method called run. So line 10 prints run.
  3. Line 4 is a getter: a function that runs when you read a property, here object.total. Its name gets get and a space in front. Line 7 uses Object.getOwnPropertyDescriptor to reach the getter function itself, and line 11 prints get total. A setter gets set in front in the same way.
  4. Line 5 uses a symbol as the method's key. A symbol is a unique key value. Symbol("secret") has the description secret. The method's name is that description in square brackets, so line 12 prints [secret].
  5. Line 8 gives the parameter fn an unnamed function as its default value. The function takes the parameter's name, so line 13 prints fn.
Names can change when a site is built

Before a site goes live, tools called bundlers and minifiers join and shrink the code. They can shorten function names, so save might become a. Use fn.name for logs and messages. Don't use it to decide what your program does, and never for security checks.

Not every function gets a name. A function made with no name nearby has an empty name, "". And a bundler can change a name later. If your code needs a name that never changes, store your own ID next to the function, like { id: "save" }.

Which property is this about?
  • bound save
  • Rest parameters are not counted
  • fn.count = 3
  • get total
  • function f(a, b = 1, c) {} is 1
  • fn.toString()
Try it yourself
0 of 6 correct

Sort each card: is it about fn.name, about fn.length, or about neither?

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

Your own properties on a function

STEP THROUGH

A function is an object, so you can add your own properties to it, like any object. For example:

  • fn.count = 0 to count how many times it was called.
  • fn.meta = {} to keep extra facts about it.
  • fn.cache = new Map() to remember results it already worked out.

These properties are public. Any code that can reach the function can read them or change them. If the data should be private, use a closure instead. A closure is a function that keeps using the variables from the place where it was made. You met closures in the Closure patterns lesson.

A function with a public counterPop out in the code editor (opens in a new tab)JavaScript
function greet(name) {  return "Hi, " + name;}greet.calls = 0;greet.calls += 1;console.log(greet("Asha"));console.log(greet.calls);
  1. Lines 1 to 3 make a function, greet, that returns a greeting.
  2. Line 4 adds a property called calls to the function and sets it to 0.
  3. Line 5 adds 1 to it, the same way as with any object property.
  4. Line 6 calls the function and prints Hi, Asha.
  5. Line 7 prints 1. Calling greet did not change calls; only line 5 did.

Any code that can reach greet can also read or change calls. So this is public information about the function, not private data.

The next example counts calls for you. makeCounter puts a count property on the function double. Then it returns a new function that adds 1 to the count each time it runs. Step through it and watch the count change.

Count calls with a function property
Step 0 of 5Ready
Your turn: follow the blue line

A function can carry your own properties. This wrapper counts calls in a property on the function.

Running in
  1. script
Next: line 2
function makeCounter(fn) {  return function counted(...args) {    fn.count += 1;    return fn(...args);  };}function double(n) { return n * 2; }const countedDouble = makeCounter(double);console.log(countedDouble(3));console.log(countedDouble(4));console.log(double.count);
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.

To memoize a function means to remember its results, so the same input is not worked out twice. The code below keeps the remembered results in a property.

Remember results in a cache propertyPop out in the code editor (opens in a new tab)JavaScript
function memoize(fn) {  function memoized(n) {    if (!memoized.cache.has(n)) {      memoized.cache.set(n, fn(n));    }    return memoized.cache.get(n);  }  memoized.cache = new Map();  return memoized;}
  1. Line 1: memoize takes the original function as fn.
  2. Line 2 makes a new function, memoized, which takes one input, n.
  3. Line 3 checks whether the cache already has an answer for n.
  4. If not, line 4 works it out with fn(n) and saves it in the cache.
  5. Line 6 returns the saved answer.
  6. Line 8 puts an empty Map called cache on memoized, as a property.

Because the cache is a property, other code can look inside it, or empty it with memoized.cache.clear(). If the cache should be private, keep the Map in a closure instead.

How to choose where to keep the data:

  • Use a property when other code should be able to see or reset the data.
  • Use a closure or a WeakMap when other code must not change it. You'll see a WeakMap near the end of this lesson.
  • If you do add a public property, give it a name that other code is unlikely to use too.

Named function expressions

A function expression is a function written where a value goes, such as on the right of =. It can have its own name, like this: const factorial = function inner(n) { … }. Now the function has two names:

  • factorial is the variable that holds the function. Code outside the function can use it.
  • inner is the function's own name. Only code inside the function can use it.
The inner name works only insidePop out in the code editor (opens in a new tab)JavaScript
const factorial = function inner(n) {  if (n <= 1) return 1;  return n * inner(n - 1);};console.log(factorial(4));try {  console.log(inner);} catch (error) {  console.log(error.name);}
  1. Line 1 saves the function in factorial. The function's own name is inner.
  2. Line 2 stops when n is 1 or less and returns 1.
  3. Line 3 calls inner from inside the function. A function that calls itself like this is using recursion.
  4. Line 5 calls factorial(4) and prints 24, which is 4 × 3 × 2 × 1.
  5. Line 7 tries to read inner from outside the function. It doesn't exist there, so it throws an error. Line 9 prints the error's name, ReferenceError.
Left, inside the function: a box called factorial holds a function card named inner. The call inner(n - 1) works there. Right, outside the function: a name tag inner leads to a ReferenceError.inside the functioninner(n)factorialinner(n - 1) worksoutside the functioninnerReferenceError
The variable factorial holds a function whose own name is inner. Inside the function, inner works, so the function can call itself. Outside, inner does not exist, so line 7 throws a ReferenceError.

The inner name has one more use. It keeps working even when the outside variable changes.

The inner name keeps working after the variable changesPop out in the code editor (opens in a new tab)JavaScript
let factorial = function inner(n) {  return n <= 1 ? 1 : n * inner(n - 1);};const saved = factorial;factorial = null;console.log(saved(4));

Line 4 saves the function in a second variable, saved. Line 5 sets factorial to null, so that name no longer holds the function. Line 6 still prints 24. The function calls itself with inner, not with factorial, so the change to factorial doesn't matter.

new Function: a function built from text

JavaScript can build a function from strings of text: new Function("a", "b", "return a + b"). The last string is the function's body. The strings before it are the parameter names.

This site never runs new Function on the page. It is here because you will see it in documentation and in older code. Two words help with the example:

  • A global variable can be seen by all the code. globalThis.shared = 3 makes one.
  • A local variable lives only inside its function, like secret inside demo.
For reading only: don't use new Function in your appPop out in the code editor (opens in a new tab)JavaScript
function demo() {  const secret = 7;  globalThis.shared = 3;  const addShared = new Function("value", "return value + shared;");  console.log(addShared(4));  try {    const readSecret = new Function("return secret;");    console.log(readSecret());  } catch (error) {    console.log(error.name);  }}demo();
  1. Line 2 makes a local variable, secret. Line 3 makes a global one, shared.
  2. Line 4 builds addShared from text. Its body reads shared.
  3. Line 5 calls it with 4. It can see the global shared, so it prints 7.
  4. Line 7 builds a function whose body reads secret.
  5. Line 8 calls it. Functions built by new Function can't see local variables, so the call throws. Line 10 prints ReferenceError.
Treat new Function like eval

new Function turns text into code and runs it, just like eval. That causes three problems:

  • If the text comes from a user, they can run any code they like on your page. That is a security hole.
  • Many sites block it with a Content Security Policy (CSP): a set of rules that tells the browser which code may run. Then new Function fails.
  • The JavaScript engine can't make this code run as fast as normal code.

It also can't see local variables, as the example shows. The tests for this lesson run the example in a separate, safe place called a sandbox.

If you need behavior that settings can change, use one of these instead: a lookup table (an object that maps names to normal functions), a normal function, or a small reader for the simple text you accept. These keep code apart from data, and they still work under a strict CSP.

Constructors for generator and async functions

A constructor is a function that makes new objects. Function is the constructor for ordinary functions. Three other kinds of functions each have their own constructor:

  • Generator functions, written function*, use GeneratorFunction.
  • Async functions, written async function, use AsyncFunction.
  • Async generator functions, written async function*, use AsyncGeneratorFunction.

These constructors are not global names, so you can't just type AsyncFunction. You almost never need them. If you do, get one from a real function, as the code below shows. It never builds code from text.

Get each constructor from a real functionPop out in the code editor (opens in a new tab)JavaScript
const GeneratorFunction = Object.getPrototypeOf(function* () {}).constructor;const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;const AsyncGeneratorFunction = Object.getPrototypeOf(async function* () {}).constructor;console.log(GeneratorFunction.name);console.log(AsyncFunction.name);console.log(AsyncGeneratorFunction.name);
  1. Line 1 makes an empty generator function. Object.getPrototypeOf gets the object it inherits from, and .constructor on that object is the function that makes generator functions.
  2. Lines 2 and 3 do the same for an async function and an async generator function.
  3. Lines 4 to 6 print GeneratorFunction, AsyncFunction, and AsyncGeneratorFunction.

Generators and async functions get their own lessons later. Here you only need to know they are function objects too.

Each page, iframe, and worker has its own copy of these constructors. An iframe is a page shown inside another page. A worker is code that runs in the background. A function made in an iframe uses that iframe's constructor, not yours. So a check like fn.constructor === AsyncFunction can fail for a function that came from an iframe.

toString: a function's code as text

fn.toString() returns a function's source text: its code, as a string. It works like this:

  • For a function you wrote, it usually returns your code.
  • For a function built into JavaScript, like Math.max, it returns a short text with [native code] in it.

On a real site, the browser doesn't get the files you wrote. A tool like Next.js turns your TypeScript and JSX into plain JavaScript first. So toString() shows that changed code, not your original file. This lesson shows its code as fixed text, and its tests check that the code works.

What toString gives backPop out in the code editor (opens in a new tab)JavaScript
function sample(a) {  return a * 2;}console.log(sample.toString().includes("return a * 2"));console.log(Math.max.toString());

Line 4 checks that the text of sample includes return a * 2, and prints true. Line 5 prints the text of Math.max, which contains [native code].

Use toString only to look

toString() is handy when you are curious or debugging. But it is not a tool for taking code apart, and it can't lead you back to your original file. Bundlers, minifiers, and built-in functions can all give text that differs from what you wrote.

Don't save that text to turn it back into a function later. Comments, spaces, changes made by build tools, and built-in markers can all make the text wrong for that job.

Wrappers that pass everything on

STEP THROUGH

A wrapper is a function that calls another function and adds a little work around the call. The extra work can be:

  • writing a log line,
  • timing the call,
  • remembering results (caching),
  • letting the function run only once.

People often call this kind of wrapper a decorator. In this lesson, decorator just means a wrapper function. It is not the @ syntax you may see in other code; that comes in a later lesson.

A good wrapper is transparent: the caller can't tell it is there. To be transparent, it must pass two things on to the original function, unchanged:

  • this, the object the method was called on.
  • Every argument the caller passed in.

So write the wrapper as a normal function, not an arrow function. A normal function gets this from the call, like cart.label(7). An arrow function takes this from the place where it was written. That only fits when you want this fixed on purpose.

Real-life analogyA gift box around a present

You put a present in a gift box and add a card. The card adds something extra. But the person still gets the same present inside.

In real life: The present inside
In JavaScript: The original function
In real life: The box and the card
In JavaScript: The wrapper's extra work, like a log line
In real life: Handing over the same present
In JavaScript: fn.apply(this, args) passes on this and every argument

Step through a logging wrapper below. It wraps the label method of a cart object.

Pass a method call through a wrapper
Step 0 of 3Ready
Your turn: follow the blue line

A decorator here is just a wrapper function. The safe version passes on this and every argument.

Running in
  1. script
Next: line 6
function withLogging(fn) {  function wrapper(...args) {    console.log("calling", fn.name, args.join(","));    return fn.apply(this, args);  }  return wrapper;}const cart = {  prefix: "Item",  label(id) { return this.prefix + " #" + id; },};cart.label = withLogging(cart.label);console.log(cart.label(7));console.log(cart.label.name);
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 the same wrapper as plain code:

A logging wrapper that keeps thisPop out in the code editor (opens in a new tab)JavaScript
function withLogging(fn) {  function wrapper(...args) {    console.log("calling", fn.name, args.join(","));    return fn.apply(this, args);  }  Object.defineProperty(wrapper, "name", { value: "logged" + fn.name[0].toUpperCase() + fn.name.slice(1) });  return wrapper;}const cart = {  prefix: "Item",  label(id) { return this.prefix + " #" + id; },};cart.label = withLogging(cart.label);console.log(cart.label(7));console.log(cart.label.name);
  1. Line 1: withLogging takes the original function as fn.
  2. Line 2 makes wrapper. Its ...args collects every argument into an array.
  3. Line 3 logs calling label 7 before the real work.
  4. Line 4 calls the original with fn.apply(this, args). apply calls a function with a this you choose and an array of arguments. Here it passes on the same this the wrapper got.
  5. Line 6: you can't change name with =, so Object.defineProperty sets it to loggedLabel.
  6. Line 13 puts the wrapper in place of cart.label.
  7. Line 14 calls cart.label(7). The wrapper gets cart as this and passes it on, so it prints Item #7. Line 15 prints loggedLabel.
cart.label(7) runs a box called wrapper, which holds the original label function. The wrapper logs, then calls the original with apply(this, args). The result is Item #7, because this is still cart.cart.label(7)label()wrapperlogs, then callsapply(this, args)"Item #7"this = cart
cart.label(7) now runs the wrapper. It logs a line, then calls the original label with fn.apply(this, args). this is still cart, so the result is "Item #7".

A wrapper must also hand back the original result. The next example has two arguments and returns a value.

Pass on every argument and the resultPop out in the code editor (opens in a new tab)JavaScript
function withTrace(fn) {  return function (...args) {    const result = fn.apply(this, args);    console.log("result", result);    return result;  };}function join(first, second) { return first + " & " + second; }const tracedJoin = withTrace(join);console.log(tracedJoin("tea", "toast"));
  1. Line 2 returns the wrapper. Its ...args collects both arguments, "tea" and "toast".
  2. Line 3 passes both to join with apply, and keeps the result.
  3. Line 4 logs result tea & toast.
  4. Line 5 returns the same result, so line 10 prints tea & toast.

The wrapper added a log line, but the caller still gets the same value.

Where you'll use this

You will meet these facts about functions in real code:

  • Logging tools read fn.name to label their messages. They still work when a name is missing.
  • Test helpers and form checkers sometimes read fn.length to guess how many inputs a function wants. It is only a hint, not a promise.
  • Memoize helpers keep their cache on the wrapper, or in a closure.
  • Frameworks, analytics tools, and permission checks wrap your callbacks. A good wrapper passes on this and every argument.
  • An ID that must never change belongs in your own data, like { id: "save" }. Don't use fn.name for it, because names can change.

The table below compares three places to keep data about a function. Read each row as: "if I keep it here, what is it good for, and what does it cost?"

Where to keep data about a function
Where you keep itWhat is it good for?What does it cost?
A property on the functionData others should see, like fn.count, fn.cache, or fn.meta.Anyone can read it or overwrite it.
A closurePrivate data, like a counter, a run-once flag, or a hidden cache.Code outside can't look at it or reset it.
A WeakMapData linked to a function, without changing the function.It takes more code, but no property names can clash.

A WeakMap is a map whose keys are objects, such as functions. Here is how it keeps data about a function outside the function:

Keep data about a function outside itPop out in the code editor (opens in a new tab)JavaScript
const labels = new WeakMap();function saveDraft() {}labels.set(saveDraft, { status: "draft" });console.log(labels.get(saveDraft).status);console.log(saveDraft.status);
  1. Line 1 makes an empty WeakMap called labels.
  2. Line 2 makes a function, saveDraft.
  3. Line 3 links saveDraft to the object { status: "draft" }.
  4. Line 4 looks it up and prints draft.
  5. Line 5 prints undefined, because the function itself never got a status property.

Library code uses this to keep its own private notes about functions. Its names can't clash with properties that other code adds.

Common misconceptions

Here are six common wrong ideas about functions. Each one comes with what is really true.

“Functions are not objects, because typeof fn is "function".”

typeof gives a special answer, "function", for objects you can call. A function is still an object. It has properties, and fn instanceof Function is true.

“length is how many arguments were passed.”

fn.length belongs to the function, and it is the same for every call. It counts the parameters before the first default value.

“fn.name is safe to use in program logic.”

Names are helpful labels, but they can change. bind adds bound and a space in front, and minifiers can shorten names. Use your own IDs for anything important.

“The inner name of a named function expression works outside it.”

The inner name works only inside the function. Outside, use the variable that holds the function.

“A wrapper can just call fn(...args).”

That call has no object in front of it, so a method loses its this. Use fn.apply(this, args) to pass this on.

“new Function is a harmless shortcut.”

It turns a string into code and runs it, like eval. Avoid it in your app, most of all with text that comes from users.

Practice: function objects

5 EXERCISES

Guess the answer before you run each program. Use the hints if you get stuck.

Exercise 1 · Warm-upPredict a name and a length

What does the second log line print? Write it exactly.

Starter codePop out in the code editor (opens in a new tab)JavaScript
function save(a, b = 1, ...rest) {}
const wrapped = save.bind(null, "draft");
console.log(save.name, save.length);
console.log(wrapped.name, wrapped.length);

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

    Exercise 2 · PracticeCount calls with a property

    Read the code. What number does the last line print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    function track(fn) {
      fn.count = 0;
      return function (...args) {
        fn.count += 1;
        return fn(...args);
      };
    }
    function shout(word) { return word.toUpperCase(); }
    const tracked = track(shout);
    tracked("hi");
    tracked("bye");
    console.log(shout.count);

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

      Exercise 3 · PracticeWrite a wrapper that keeps this

      The wrapper should pass the call on, so the method still sees its object. What does the last line print?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function logCalls(fn) {
        return function (...args) {
          return fn.apply(this, args);
        };
      }
      const user = {
        prefix: "User",
        label(id) { return this.prefix + ":" + id; },
      };
      user.label = logCalls(user.label);
      console.log(user.label(5));

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

        Exercise 4 · ChallengeWhat can new Function see?

        Write the two printed lines. Why is the first one a number and the second one an error name?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        function demo() {
          const secret = 7;
          globalThis.shared = 3;
          const readShared = new Function("return shared;");
          console.log(readShared());
          try {
            const readSecret = new Function("return secret;");
            console.log(readSecret());
          } catch (error) {
            console.log(error.name);
          }
        }
        demo();

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

          Exercise 5 · ChallengeFix a wrapper that loses this

          The wrapper should pass the call on, so box.getValue() still works. What does the fixed code print?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          function wrap(fn) {
            return function (...args) {
              return fn.apply(this, args);
            };
          }
          const box = {
            value: 9,
            getValue() { return this.value; },
          };
          box.getValue = wrap(box.getValue);
          console.log(box.getValue());

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

            Quiz: check your understanding

            7 QUESTIONS

            Try to guess each output before you click. For each code question, first ask what is printed: a name, a length, or the result of a wrapper. Every answer explains why it is right or wrong.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What name and length does this function get?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const greet = function (name, punctuation = "!") {};
              console.log(greet.name, greet.length);

              Choose an answer to see the explanation.

            2. Question 2 of 7Which is true about your own properties on a function?

              Choose an answer to see the explanation.

            3. Question 3 of 7What name does the bound method get?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const obj = { run() {} };
              const again = obj.run.bind(obj);
              console.log(again.name);

              Choose an answer to see the explanation.

            4. Question 4 of 7Why is new Function risky in your app?

              Choose an answer to see the explanation.

            5. Question 5 of 7Which line makes a wrapper safe for methods?

              Choose an answer to see the explanation.

            6. Question 6 of 7What name does the method with a symbol key get?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const key = Symbol("job");
              const tasks = { [key]() {} };
              console.log(tasks[key].name);

              Choose an answer to see the explanation.

            7. Question 7 of 7What does fn.toString() usually give back?

              Choose an answer to see the explanation.

            Key takeaways

            • A function is an object you can call. You can run it, and you can read and add properties.
            • name is the function's name. length counts the parameters before the first default value. Rest parameters are never counted.
            • An unnamed function takes a name from where it is saved: a variable, an object key, a getter, a default value, or a symbol. bind adds bound in front.
            • Your own properties on a function are public. Use a closure for private data.
            • The inner name of a named function expression works only inside the function.
            • new Function turns text into code. It can't see local variables. Avoid it in your app.
            • A good wrapper passes the call on with fn.apply(this, args).

            Remember the one-liner.
            A function is an object you can call: you can run it, read its properties, add your own, and wrap it.

            Up next: Methods on primitives. It explains how simple values, like strings, seem to have methods.

            CompleteFrontend Clear concepts. Working examples.