The apply method in JavaScript is similar to call, except that it takes arguments as an array.
1οΈβ£ Understanding Function.prototype.apply
Native behavior:
function greet(age, city) { console.log(`Hello, my name is ${this.name}, I am ${age} years old, and I live in ${city}.`); } const person = { name: "Alice" }; greet.apply(person, [25, "New York"]);
π Difference from call :
call(context, arg1, arg2, arg3, ...)β Arguments are passed individually.apply(context, [arg1, arg2, arg3, ...])β Arguments are passed as an array.
2οΈβ£ Polyfill for apply
We need to:
- Attach the function to the
thisArg(context). - Pass the arguments as an array.
- Remove the temporary function reference.
Function.prototype.myApply = function (context, args) { if (typeof this !== "function") { throw new TypeError("myApply can only be used on functions"); } context = context || globalThis; // Default to global (window in browser, global in Node.js) const fnKey = Symbol(); // Unique key to avoid property collisions context[fnKey] = this; // Assign function to context const result = context[fnKey](...(args || [])); // Invoke function with spread operator delete context[fnKey]; // Cleanup return result; };
3οΈβ£ Testing the Polyfill
function greet(age, city) { console.log(`Hello, my name is ${this.name}, I am ${age} years old, and I live in ${city}.`); } const person = { name: "Bob" }; greet.myApply(person, [30, "Los Angeles"]); // Output: Hello, my name is Bob, I am 30 years old, and I live in Los Angeles.
4οΈβ£ Dry Run Step-by-Step
Let's break it down for:
greet.myApply(person, [30, "Los Angeles"]);
π Step 1: Call myApply
greet.myApply(person, [30, "Los Angeles"]);
thisinsidemyApplyrefers togreet.context = person({ name: "Bob" }).args = [30, "Los Angeles"].
π Step 2: Function Definition Breakdown
Inside myApply:
Function.prototype.myApply = function (context, args) { if (typeof this !== "function") { throw new TypeError("myApply can only be used on functions"); }
β
this is greet (a function), so no error.
π Step 3: Set context
context = context || globalThis;
context = person.
π Step 4: Attach Function Temporarily
const fnKey = Symbol(); context[fnKey] = this;
- A unique property is added to
person:{ name: "Bob", [Symbol(fnKey)]: function greet(age, city) { ... } }
π Step 5: Invoke Function
const result = context[fnKey](...(args || []));
- Equivalent to:
person ; - Since
thisis nowperson, it prints:Hello, my name is Bob, I am 30 years old, and I live in Los Angeles.
π Step 6: Cleanup
delete context[fnKey];
- Removes the temporary function from
person, restoring it to:{ name: "Bob" }
π Final Execution Summary
| Step | Action |
|---|---|
| 1οΈβ£ | greet.myApply(person, [30, "Los Angeles"])is called |
| 2οΈβ£ | context = person |
| 3οΈβ£ | Symbol(fnKey)is created and assigned to person[Symbol(fnKey)] = greet |
| 4οΈβ£ | person is invoked, printing "Hello, my name is Bob, I am 30 years old, and I live in Los Angeles." |
| 5οΈβ£ | Temporary function property is deleted |
β
Works just like the native apply method!
Quick Quiz
Test your understanding with 3 quick questions
Q1What is the main difference between `call()` and `apply()`?
Q2Why is a `Symbol` used as the function key in the polyfill?
Q3What does `apply()` return?