cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Function calls: [[Call]] & [[Construct]]

Trace how ECMAScript calls and constructs functions, binds this, initializes parameters, handles classes and bound functions, and routes Proxy traps.

By the end, you can
  • 01
    Trace a callExplain context preparation, this binding, parameter instantiation, and body evaluation.
  • 02
    Trace constructionTell base and derived constructors apart, and explain new.target and instance prototypes.
  • 03
    Recognize wrappersPredict bound-function and Proxy behavior with calls, new, and Reflect.

Two ways to invoke a function

In the previous execution contexts lesson, a call put a new context on the stack. Here we ask what starts that call. The specification gives a function object an internal method called [[Call]]. A function that can be used with new also has [[Construct]].

One ordinary callPop out in the code editor (opens in a new tab)JavaScript
function greet(user) {
  return "Hi, " + user;
}
console.log(greet("Asha"));

Line 1 defines greet with one parameter. Line 2 returns a greeting made with that parameter. Line 4 calls it with "Asha" and prints Hi, Asha. The call starts greet's evaluation; the source does not invoke [[Call]] by name.

Definition

An internal method is a specification operation on an object, written in double brackets. Ordinary function calls use [[Call]]; construction uses [[Construct]] when that method exists. Neither is a JavaScript property you can read as greet["[[Call]]"].

These are two paths through function behavior, not two kinds of parentheses. An ordinary call can return a number, string, or object. Construction must return an object or throw. We will use tiny programs to observe the results, while keeping hidden spec state separate from values available to your code.

Function objects and their internal slots

A function object holds callable behavior plus state the specification needs. An internal slot is a named piece of that spec state. Its name is not a normal property key. A regular function declaration also gets construction behavior; not every callable function does.

A function can also constructPop out in the code editor (opens in a new tab)JavaScript
function Order(item) {
  this.item = item;
}
console.log(typeof Order, new Order("tea").item);

Line 1 declares Order. Line 2 assigns its item when the function runs with an object as this. Line 4 prints function tea: typeof Order is function, and new Order("tea") creates an instance whose item is tea. That output shows behavior, not the contents of any hidden slot.

Some internal slots on ECMAScript function objects
SlotWhy the spec keeps itWhat not to assume
[[Environment]]The environment captured when the function was created.Not a property you can read from the function.
[[FormalParameters]]The parsed parameter list used when a call starts.Not the arguments array or the public length property.
[[ECMAScriptCode]]The parsed body selected for evaluation.Not a string that is evaluated anew on each call.
[[ThisMode]]LEXICAL, STRICT, or GLOBAL selection for this.Not the caller's strictness or the function's name.
[[ConstructorKind]]BASE or DERIVED when the function is constructed.Not the public constructor property on an instance.

The current ECMAScript function-object table also lists [[Realm]], [[ScriptOrModule]], and [[IsClassConstructor]]. The captured environment helps a function find outer names. Parsed parameters and code tell the call which body to evaluate. These are spec devices, not a promise about a particular engine's memory layout.

Real-life analogyA home kitchen and its cook

A cook knows a recipe before an order arrives. Each order gives ingredients for one dish. A function similarly keeps its code between calls, while each call supplies new inputs.

In real life: The cook knows a recipe
In JavaScript: A function stores code to run
In real life: An order lists ingredients
In JavaScript: Arguments provide inputs
In real life: The cook makes the dish
In JavaScript: A call evaluates the body

Where the analogy stops: The cook is a person; an internal slot is specification state and need not be a stored object.

Prepare a call and bind this

PrepareForOrdinaryCall sets up a new function execution context and local function environment. The caller waits while the callee runs. OrdinaryCallBindThis then handles the value of this, depending on the function's [[ThisMode]]. Only after that does the call evaluate the body.

A call returns a fee-added pricePop out in the code editor (opens in a new tab)JavaScript
function addFee(price) {
  return price + 2;
}
console.log(addFee(3));

Line 1 defines addFee with a parameter named price. Line 2 adds 2 and returns the sum. Line 4 passes 3 and prints 5. In spec terms, preparing the call gives this invocation its own place to bind price; it does not reuse another call's local parameter.

Quoted steps: ordinary [[Call]]Text
"Let calleeContext be PrepareForOrdinaryCall(func, undefined)."
"Perform OrdinaryCallBindThis(func, calleeContext, thisArg)."
"Let result be Completion(OrdinaryCallEvaluateBody(func, argList))."

These selected lines come from the ordinary function [[Call]] algorithm. The surrounding steps handle class calls, completion records, and restoring the caller. The excerpt is spec text, not JavaScript to run. In particular, a thrown error also restores the caller's context.

Replay an ordinary call
Step 0 of 4Ready
Your turn: follow the blue line

Replay of the lesson's instrumented addFee call; this is not an engine debugger.

Running in
  1. script
Next: line 4
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
function addFee(price) {  return price + 2;}
CallStoreChangeResultRun = next line. 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.

Step through the real addFee invocation. First the caller calls, then price is stored, then the body returns 5, and finally the caller prints it. This is a replay of instrumented lesson code, not a debugger reading the engine's context stack. Back shows the earlier saved snapshot rather than a later value.

A method receiver and an explicit receiverPop out in the code editor (opens in a new tab)JavaScript
const cart = { price: 3, show() { return this.price; } };
console.log(cart.show());
console.log(Reflect.apply(cart.show, { price: 5 }, []));

Line 1 makes a cart with a method that reads this.price. Line 2 calls it through cart and prints 3. Line 3 passes another object as the receiver to Reflect.apply and prints 5. The method's call still runs the same body; its supplied this changes.

Three this modes

STRICT keeps the supplied this unchanged. A bare strict call receives undefined. GLOBAL, the mode for an ordinary non-strict function, replaces null or undefined with its realm's global this and wraps other primitive receivers. LEXICAL means an arrow does not bind a new this at all; it uses the enclosing one.

Strict this stays undefinedPop out in the code editor (opens in a new tab)JavaScript
function strictThis() {
  "use strict";
  return this;
}
console.log(strictThis() === undefined);

Line 1 defines strictThis. Line 2 switches its body to strict mode. Line 3 returns this unchanged; line 5 prints true because the unbound call has undefined as its receiver. The check avoids relying on a browser-specific global object name.

Non-strict this substitutes a receiverPop out in the code editor (opens in a new tab)JavaScript
function looseThis() {
  return this;
}
console.log(looseThis.call(null) === globalThis);
console.log(typeof looseThis.call(3));

Lines 1 to 3 define a non-strict function that returns its receiver. Line 4 passes null and prints true: the function uses globalThis. Line 5 passes 3 and prints object, because the number is boxed. The replacement happens when the callee binds this, not when .call receives the input.

An arrow keeps the enclosing thisPop out in the code editor (opens in a new tab)JavaScript
const cart = { price: 3, makeReader() { return () => this.price; } };
const readPrice = cart.makeReader();
console.log(readPrice.call({ price: 5 }));

Line 1 defines a cart whose method makes an arrow. Line 2 calls that method through the cart and saves the arrow. Line 3 tries to supply a different receiver, but prints 3. The arrow reads the cart from its enclosing method call. Its lexical this is why changing the later call receiver has no effect.

See OrdinaryCallBindThis and OrdinaryFunctionCreate for the exact branches. The modes describe the function being called, not whether its caller happened to write "use strict" elsewhere.

OrdinaryCallEvaluateBody selects the body

OrdinaryCallEvaluateBody hands the function's parsed [[ECMAScriptCode]] to the EvaluateBody operation. EvaluateBody has different cases for plain functions, arrows, generators, and async functions. We start with the plain case so each step has one job.

Return the supplied itemPop out in the code editor (opens in a new tab)JavaScript
function getItem(item) {
  return item;
}
console.log(getItem("tea"));

Line 1 declares getItem with a parameter. Line 2 returns that parameter. Line 4 calls it with "tea" and prints tea. To reach line 2, JavaScript must already have given the parameter its value. The function body is evaluated for this invocation, not stored as a string and reparsed by the example.

Quoted steps: evaluating a plain function bodyText
"Return ? EvaluateBody of func.[[ECMAScriptCode]] with arguments func and argList."
"Perform ? FunctionDeclarationInstantiation(funcObj, argList)."

OrdinaryCallEvaluateBody delegates to the appropriate EvaluateBody operation. For a plain function, EvaluateFunctionBody performs FunctionDeclarationInstantiation before body statements. The short excerpt quotes one step from each algorithm; the full spec also specifies returns and abrupt completion.

This distinction helps when reading a more advanced algorithm. The call method sets up the context and this. EvaluateBody selects the right kind of body. Binding parameters and declarations is part of entering that body. None of those names is a JavaScript API your app should call.

FunctionDeclarationInstantiation: hoisting, precisely

FunctionDeclarationInstantiation prepares bindings for a particular invocation before its ordinary body statements run. It initializes parameters and function declarations, creates var bindings initialized to undefined, and creates lexical bindings that remain uninitialized until their declarations execute. That is the useful meaning behind the informal word hoisting.

A function is ready before a var assignmentPop out in the code editor (opens in a new tab)JavaScript
function receipt(item) {
  console.log(label(item), fee);
  var fee = 2;
  function label(value) { return "Item: " + value; }
}
receipt("tea");

Line 1 starts the function. Line 2 calls label(item) and reads fee. Line 3 assigns 2 to fee, but has not run when line 2 logs. Line 4 declares label; it was initialized during setup. Line 6 passes tea. The output is Item: tea undefined.

The specification's FunctionDeclarationInstantiation distinguishes parameter names, var names, lexical names, and function declarations. It processes function declarations so the last declaration for a duplicate name wins where duplicates are permitted. That is more precise than saying all code "moves to the top": the assignment on line 3 stays in its original execution order.

A default parameter runs during binding setupPop out in the code editor (opens in a new tab)JavaScript
function total(price = 3) {
  return price + 2;
}
console.log(total());

Line 1 gives price a default of 3. Line 2 returns the value plus 2. Line 4 calls without an argument, so the default runs while parameters are initialized and the output is 5. When default parameter expressions exist, the spec may create a separate environment for body declarations. A closure made in a default initializer should not quietly see a later body declaration.

A lexical binding is not usable earlyPop out in the code editor (opens in a new tab)JavaScript
function readEarly() {
  console.log(typeof price);
  let price = 3;
}
try { readEarly(); } catch (error) { console.log(error.name); }

Line 1 declares readEarly. Line 2 attempts to inspect price before line 3 initializes it. Line 5 calls the function and catches the error, printing ReferenceError. Even typeof cannot read a local lexical binding before initialization. This is different from the var fee case above.

A useful reading order

Look at parameter setup first, then which declarations get bindings, then which bindings get values before the body. After that, read the statements in source order. A binding can exist before the statement that assigns it.

[[Construct]] and new.target

A constructor is a function object with a [[Construct]] method. A new expression uses that method and supplies a newTarget: the constructor selected for this construction. For an ordinary base constructor, the spec creates an instance first, binds it as this, and then runs the body.

Create an Order instancePop out in the code editor (opens in a new tab)JavaScript
function Order(item) {
  this.item = item;
}
console.log(new Order("tea").item);

Line 1 declares Order. Line 2 puts the argument on this.item. Line 4 constructs it with "tea" and prints tea. The object is not created by line 2: construction supplies an object for that line to update. A plain call to this non-strict function would not create the same instance.

Quoted steps: ordinary [[Construct]]Text
"Let kind be func.[[ConstructorKind]]."
"Let thisArg be ? OrdinaryCreateFromConstructor(newTarget, "%Object.prototype%")."
"Let calleeContext be PrepareForOrdinaryCall(func, newTarget)."

The function [[Construct]] algorithm branches on [[ConstructorKind]]. For a base constructor it uses OrdinaryCreateFromConstructor, which reads the prototype property from newTarget. If that property is not an object, a realm-specific intrinsic prototype is used. This excerpt omits instance-field initialization and return-completion rules.

Replay an ordinary construction
Step 0 of 4Ready
Your turn: follow the blue line

Replay of the lesson's instrumented Order constructor; it does not inspect internal slots.

Running in
  1. script
Next: line 4
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
function Order(item) {  this.item = item;}console.log(order.item);
CallStoreChangeResultRun = next line. 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.

Follow the instrumented construction: line 4 starts it, line 1 receives the argument, line 2 writes to the new object, and line 5 reads its property. The actual Order constructor runs when the recording is made. The frames explain the observable program, not a direct inspection of [[Construct]] or engine memory.

Change the order item
Constructor source with the initial itemPop out in the code editor (opens in a new tab)JavaScript
function Order(item) { this.item = item; }
const order = new Order("tea");
console.log(order.item);
Result of construction
order.item"tea"

The input replaces the string on line 2 of the displayed source.

Try it yourself

new Order("tea") stores "tea" on the instance.

This runs the lesson's real constructor with your item. It does not inspect an engine's internal slots. Reset restores tea.

Change only the item above, then compare the instance's property. Reset restores tea. The visible source shows the initial value on line 2; the control substitutes your input for that one value when it constructs another real instance. Empty text is still a valid item and appears as an empty string in the result.

Read new.target from inside OrderPop out in the code editor (opens in a new tab)JavaScript
function Order(item) {
  this.item = item;
  this.createdBy = new.target.name;
}
console.log(new Order("tea").createdBy);

Line 1 defines Order. Line 2 writes its item; line 3 records new.target.name. Line 5 constructs Order and prints Order. For this direct new expression, new.target and the function whose body runs are the same function. That need not hold when another newTarget is supplied.

Choose a different instance prototypePop out in the code editor (opens in a new tab)JavaScript
function Order(item) { this.item = item; }
function SpecialOrder() {}
SpecialOrder.prototype.kind = "gift";
const order = Reflect.construct(Order, ["tea"], SpecialOrder);
console.log(order.item, order.kind, order instanceof SpecialOrder);

Line 1 defines Order. Line 2 defines SpecialOrder. Line 3 gives its prototype kind: "gift". Line 4 uses Reflect.construct with Order as the body and SpecialOrder as newTarget. Line 5 prints tea gift true: the body wrote tea, while the new instance inherits kind through SpecialOrder.prototype.

Real-life analogyA shopping list

You choose a shopping-list format before writing tea on it. The format and the item answer different questions. Construction similarly separates the chosen instance prototype from the body that writes properties.

In real life: Choose a list format
In JavaScript: newTarget supplies a prototype
In real life: Write tea on the list
In JavaScript: The constructor body writes item
In real life: Read the finished list
In JavaScript: Construction returns an object

Where the analogy stops: A real shopping list has no prototype chain; the analogy only separates the chosen format from what gets written.

Base and derived class constructors

A base class constructor has the BASE constructor kind. Like a regular base constructor, it receives an object before its body assigns properties. A derived class constructor, created with extends, has the DERIVED kind: its this starts uninitialized until super() constructs the parent and establishes the binding.

A base class gets an instancePop out in the code editor (opens in a new tab)JavaScript
class Order {
  constructor(item) { this.item = item; }
}
console.log(new Order("tea").item);

Line 1 declares Order. Line 2 receives item and writes it to this. Line 4 constructs an instance and prints tea. The class definition is strict code, but strictness does not prevent its constructor from using this when it is properly constructed.

A derived class calls super firstPop out in the code editor (opens in a new tab)JavaScript
class Order { constructor(item) { this.item = item; } }
class GiftOrder extends Order {
  constructor(item) {
    super(item);
    this.gift = true;
  }
}
console.log(new GiftOrder("tea").item);

Line 1 defines the base Order. Line 2 extends it. Line 4 calls super(item), which makes the base instance. Line 5 can then write gift on the resulting this. Line 8 constructs the derived class and prints tea. The newTarget remains the derived class through the super construction.

In the ordinary [[Construct]] algorithm, only the BASE branch makes an object before evaluating its own body. The derived function environment begins with an uninitialized this binding. The super call constructs the superclass with the same newTarget and binds the resulting object.

Reading this before super fails on purposePop out in the code editor (opens in a new tab)JavaScript
class Order {}
class GiftOrder extends Order {
  constructor() {
    this.gift = true;
    super();
  }
}
new GiftOrder();

Line 1 defines a base class. Line 2 declares its subclass. Line 4 tries to use this before line 5 calls super(). Line 8 starts construction. The operation throws a ReferenceError before the assignment can finish. The exact message varies by engine, so rely on the error type.

Callable is not the same as constructable

A class constructor has a [[Call]] method, but that method throws a TypeError for an ordinary call without new. An arrow or a concise method can be called, but lacks [[Construct]]. Trying new on either throws a TypeError before its function body starts. The error messages are not standardized text.

Calling a class without new fails on purposePop out in the code editor (opens in a new tab)JavaScript
class Order {}
Order();

Line 1 declares the class. Line 2 invokes it without new, so it throws TypeError. This is not the same as lacking [[Call]]: the class call method itself detects that it must reject a call.

Constructing an arrow fails on purposePop out in the code editor (opens in a new tab)JavaScript
const makeOrder = (item) => ({ item });
new makeOrder("tea");

Line 1 makes a callable arrow. Line 2 attempts to construct it and throws TypeError. The arrow's ordinary call would return an object, but returning an object does not install [[Construct]] on the function.

A concise method is callablePop out in the code editor (opens in a new tab)JavaScript
const cart = { show() { return "tea"; } };
console.log(cart.show());
console.log(Object.hasOwn(cart.show, "prototype"));

Line 1 defines a concise show method. Line 2 calls it and prints tea. Line 3 checks for an own prototype property and prints false. The method is not a constructor; do not confuse this with a regular function expression merely stored as a property. The method-definition algorithms do not make this concise method a constructor.

Bound function exotic objects

Function.prototype.bind creates a bound function exotic object: a wrapper around a callable target. It remembers a [[BoundTargetFunction]], a [[BoundThis]] value, and [[BoundArguments]]. An ordinary call prepends those arguments and forwards the saved receiver.

Bind a receiver and an itemPop out in the code editor (opens in a new tab)JavaScript
function show(item) { return this.user + ": " + item; }
const forAsha = show.bind({ user: "Asha" }, "tea");
console.log(forAsha.call({ user: "Ravi" }));

Line 1 defines show, which reads this.user. Line 2 binds Asha as the receiver and tea as the first argument. Line 3 tries to call with another receiver but prints Asha: tea. The later receiver does not displace the bound receiver.

Quoted steps: bound constructionText
"Let args be the list-concatenation of boundArgs and argList."
"If SameValue(func, newTarget) is true, set newTarget to target."
"Return ? Construct(target, args, newTarget)."

The bound function algorithms give the wrapper [[Construct]] only when its target is constructable. During construction, the saved receiver is not used. If the wrapper itself is newTarget, the algorithm replaces it with the target before forwarding. These quoted steps describe the construction path, not the ordinary call path shown above.

A bound constructor still constructsPop out in the code editor (opens in a new tab)JavaScript
function Order(item) { this.item = item; }
const TeaOrder = Order.bind({ item: "ignored" }, "tea");
const order = new TeaOrder();
console.log(order.item, order instanceof Order);

Line 1 defines Order. Line 2 binds tea and an ignored receiver. Line 3 constructs via TeaOrder. Line 4 prints tea true. The item was prepended, while the new instance inherited from Order.prototype. The bound wrapper itself does not have an own prototype property.

Binding an arrow cannot add constructionPop out in the code editor (opens in a new tab)JavaScript
const makeOrder = (item) => ({ item });
const TeaOrder = makeOrder.bind(null, "tea");
try { new TeaOrder(); } catch (error) { console.log(error.name); }

Line 1 defines an arrow. Line 2 binds a first argument to it. Line 3 tries new but catches the error, printing TypeError. Binding does not grant [[Construct]] to a target that lacks it. This also shows why the presence or absence of a public prototype property is not a reliable constructor test.

Proxy traps and Reflect forwarding

A Proxy can intercept a callable target through an apply trap. That trap receives the target, this argument, and argument array. A constructable target can also have a construct trap, which receives the target, argument array, and newTarget. Proxy creation cannot add either ability to a target that lacks it.

The apply trap changes a greetingPop out in the code editor (opens in a new tab)JavaScript
function greet(user) { return "Hi, " + user; }
const traced = new Proxy(greet, {
  apply(target, thisArg, args) {
    return Reflect.apply(target, thisArg, args).toUpperCase();
  },
});
console.log(traced("Asha"));

Line 1 defines greet. Line 2 wraps it. Line 3 handles ordinary calls; line 4 forwards them with Reflect.apply and uppercases the result. Line 7 calls the proxy and prints HI, ASHA. Forwarding keeps the original function's arguments and receiver intact until the trap deliberately transforms the result.

Quoted steps: Proxy call and constructionText
"Let trap be ? GetMethod(handler, "apply")."
"Let trap be ? GetMethod(handler, "construct")."
"If newObj is not an Object, throw a TypeError exception."

These lines come from Proxy internal methods. The apply trap implements Proxy [[Call]] when present; construct implements Proxy [[Construct]]. If a trap is absent, the relevant target method is invoked instead. A revoked Proxy throws before forwarding to either one.

The construct trap forwards newTargetPop out in the code editor (opens in a new tab)JavaScript
function Order(item) { this.item = item; }
const traced = new Proxy(Order, {
  construct(target, args, newTarget) {
    return Reflect.construct(target, args, newTarget);
  },
});
console.log(new traced("tea").item);

Line 1 defines Order. Line 2 wraps it. Line 3 intercepts new, and line 4 forwards the arguments and newTarget. Line 7 constructs and prints tea. The trap does not need to create an instance by hand: Reflect.construct does the requested construction.

A primitive construct result fails on purposePop out in the code editor (opens in a new tab)JavaScript
const Order = new Proxy(function () {}, {
  construct() { return 3; }
});
new Order();

Line 1 wraps a constructable function. Line 2 returns the primitive 3 from the construct trap. Line 4 starts construction and throws TypeError. The rule is about the trap's returned type; the engine's exact error message is not part of our example.

Reflect.apply and Reflect.construct

Reflect.apply performs a call with an explicit this value and an array-like argument list. Reflect.construct performs construction with arguments and an optional different newTarget. These are real JavaScript APIs. The internal methods they request are still not directly readable properties.

Call with a chosen receiverPop out in the code editor (opens in a new tab)JavaScript
function show(item) { return this.user + ": " + item; }
console.log(Reflect.apply(show, { user: "Asha" }, ["tea"]));

Line 1 defines show, which reads its receiver's user. Line 2 gives it Asha and an argument array holding tea. It prints Asha: tea. Compare this with the earlier Reflect.construct(Order, ["tea"], SpecialOrder) example: the latter chooses an instance prototype through newTarget, rather than choosing this for an ordinary call.

Calls in a checkout page

Imagine a checkout page with a cart price and a small fee. A method can read its receiver's price, then pass that value to a plain function that computes the total. You do not need to write any internal method yourself; knowing the difference between a method receiver and a parameter makes the app code easier to debug.

A checkout totalPop out in the code editor (opens in a new tab)JavaScript
function showTotal(price) { return price + 2; }
const checkout = { price: 3, show() { return showTotal(this.price); } };
console.log(checkout.show());

Line 1 defines showTotal to add 2. Line 2 creates checkout with a price of 3 and a method that passes this.price to that function. Line 3 calls the method through checkout and prints 5. The method call supplies checkout as this; the nested function receives 3 as price.

Passing checkout.show directly to another API could lose that receiver. If the callback must keep checkout, pass an arrow that calls checkout.show() or bind the method to checkout. There is no need for a Proxy just to preserve a callback's this. Proxy traps are better saved for cases that actually need to intercept calls.

Which invocation can each function handle?
  • An arrow such as item => item
  • The concise method cart.show
  • A function Order(item) {} declaration
  • Order.bind(null, "tea")
  • A class Order
  • A class GiftOrder extends Order
Try it yourself
0 of 6 correct

Sort each function by its call and construction behavior. Think about the internal method, not typeof.

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

A class belongs in the third category even though its class constructor has a [[Call]] method: that method immediately throws. The sorter describes useful successful behavior rather than pretending the method is absent. The bound constructor shows the opposite surprise: no own prototype property, but new works.

Easy mistakes to avoid

A value may look like a function without supporting every kind of invocation. Start with what the object can do, then ask what this particular call attempts. Public properties can help explain a familiar case, but they cannot replace the spec's internal-method distinction.

A method call is not constructionPop out in the code editor (opens in a new tab)JavaScript
const cart = { show() { return "tea"; } };
console.log(cart.show());
console.log(Object.hasOwn(cart.show, "prototype"));

Line 1 defines the method. Line 2 calls it normally and prints tea. Line 3 prints false for an own prototype property. That fact is consistent with a concise method lacking [[Construct]], but the bound constructor above shows why the property check is not a universal test.

  • "Every callable works with new." Arrows and concise methods are callable but not constructors.
  • "A class has no [[Call]]." Its [[Call]] exists and throws TypeError when invoked without new.
  • "No own prototype means new must fail." Bound functions can construct if their target can.
  • "Hoisting runs the assignment early." A var binding gets undefined before its assignment runs.
  • "A Proxy trap can make any target callable." The target determines the proxy's call capability.
Keep capability, property, and newTarget separate
IdeaMeaningImportant limit
CallableHas [[Call]]May lack [[Construct]], as arrows and methods do.
ConstructableHas [[Construct]]Class constructors can have [[Call]] yet reject ordinary calls.
prototype propertyUsually supplies an instance prototype for normal constructorsAbsence alone cannot prove non-constructability: bound constructors lack it.
new.targetThe constructor originally selected for constructionNot always the function currently executing as the body.

If new unexpectedly fails, verify the target function's kind. If an instance has the wrong prototype, check the newTarget passed through Reflect.construct or a Proxy construct trap. If a method reads the wrong property, check which this argument the call actually supplies.

Practice exercises

Read each snippet first, then run it and enter the printed output. The tasks move from one call to declaration setup, construction, and a small checkout scenario. Use the hints one at a time; the worked solution shows the code and the reason behind its result.

Exercise 1 · Warm-upPredict an ordinary call

A checkout helper adds a small fee to a price. Read the call at the bottom, then trace the return statement. What single number appears in the console?

Starter codePop out in the code editor (opens in a new tab)JavaScript
function addFee(price) {
  return price + 2;
}
console.log(addFee(3));

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

    Exercise 2 · Warm-upPredict declaration setup

    A receipt helper uses label before its declaration and reads fee before its assignment. Type the entire console line. Explain why one name works while the other still has its initial value.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    function receipt(item) {
      console.log(label(item), fee);
      var fee = 2;
      function label(value) { return "Item: " + value; }
    }
    receipt("tea");

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

      Exercise 3 · PracticeCheck strict this

      A callback needs to know whether an unbound call has a receiver. Run the strict-mode example and type the boolean it prints. Which part of the call process decides that value?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function strictThis() {
        "use strict";
        return this;
      }
      console.log(strictThis() === undefined);

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

        Exercise 4 · PracticeFollow a new instance

        Imagine an order card created for tea. Follow new Order and identify which object line 2 changes. What does the final property read print?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        function Order(item) {
          this.item = item;
        }
        console.log(new Order("tea").item);

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

          Exercise 5 · ChallengeConstruct a bound Order

          A page preselects tea before creating an order. Predict both values printed by the bound constructor example. Does the saved receiver object become the new instance?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          function Order(item) { this.item = item; }
          const TeaOrder = Order.bind({ item: "ignored" }, "tea");
          const order = new TeaOrder();
          console.log(order.item, order instanceof Order);

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

            Exercise 6 · ChallengeApply calls on a real checkout page

            Your checkout page displays the final price from a cart method. Trace the receiver on the method call, then the argument passed into the helper. What number will the customer see in the console?

            Starter codePop out in the code editor (opens in a new tab)JavaScript
            function showTotal(price) { return price + 2; }
            const checkout = { price: 3, show() { return showTotal(this.price); } };
            console.log(checkout.show());

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

              Check your understanding

              Decide whether each example makes an ordinary call or constructs an instance. Then resolve parameters and this before reading the body. For class and Proxy questions, distinguish the capability of the target from what its call or construct path does with that capability.

              Function calls in the spec quiz · 8 questionsScore: first tries count
              1. Question 1 of 8Which internal method starts an ordinary function call?

                Choose an answer to see the explanation.

              2. Question 2 of 8What does this ordinary call print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                function addFee(price) { return price + 2; }
                console.log(addFee(3));

                Choose an answer to see the explanation.

              3. Question 3 of 8When does FunctionDeclarationInstantiation happen for a plain body?

                Choose an answer to see the explanation.

              4. Question 4 of 8What does this hoisting example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                function receipt() { console.log(fee); var fee = 2; }
                receipt();

                Choose an answer to see the explanation.

              5. Question 5 of 8What does this construction check print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                function Order() { this.item = "tea"; }
                console.log(new Order().item);

                Choose an answer to see the explanation.

              6. Question 6 of 8What happens when a derived constructor reads this before super()?

                Choose an answer to see the explanation.

              7. Question 7 of 8What does binding a constructable Order preserve?

                Choose an answer to see the explanation.

              8. Question 8 of 8What must a Proxy construct trap return?

                Choose an answer to see the explanation.

              A wrong answer is a useful clue: look at the exact point where the two paths diverge. For example, a var binding initialized to undefined is not the same as a lexical binding awaiting initialization, and a class call throwing is not the same as a missing [[Call]] method.

              Key takeaways

              An invocation is easier to read when you separate its setup from its body. The call or construct method chooses the path; environment setup gives each invocation its bindings; the body then runs with those values. Wrappers may forward a path without changing the target's original capability.

              • [[Call]] prepares a function context, binds this according to [[ThisMode]], and evaluates the body.
              • FunctionDeclarationInstantiation sets up parameters, var, lexical, and function bindings before statements run.
              • [[Construct]] receives newTarget; a base constructor creates its instance before evaluating the body.
              • A derived constructor needs super before using this; a class rejects ordinary calls without new.
              • Arrows and concise methods have no [[Construct]]. Bound wrappers preserve construction only when their targets have it.
              • Proxy apply and construct traps intercept different paths. Reflect can forward each path with explicit inputs.

              Remember the one-liner.
              [[Call]] invokes; [[Construct]] builds an object with a newTarget.

              Coming next: Internal methods & exotic objects. We will use the same internal-method vocabulary to understand property access and unusual objects. Return to execution contexts whenever the call setup itself feels unfamiliar.

              CompleteFrontend Clear concepts. Working examples.