call( ) polyfill
Prompt
Implement a polyfill for the JavaScript .call() method. Add a myCall method to Function.prototype that works exactly like the native .call(), without using .call(), .apply(), or .bind().
Playground
You can't use .call(), .apply(), or .bind(). But
there's another way to control what this is inside a
function: call it as a method on an object. If you write
obj.fn(), then this inside fn is obj.
Temporarily attach the function as a property on the
context object, call it, then clean up by deleting the
property. Use a Symbol for the property name to avoid
colliding with existing properties.
Solution
Explanation
To write this polyfill, you first need to understand what .call() actually does. It lets you borrow a function and run it with a different this.
For example, say you have a greet function that uses this.name. Normally, this depends on how you call the function. But with .call(), you can say "run this function, but pretend this is this specific object."
greet.call(person, 'Hello');
// "this" inside greet = personSo .call() does two things: it sets the this context, and it executes the function immediately (unlike .bind() which returns a new function for later).
The core trick
The tricky part is: how do you control what this is without using .call(), .apply(), or .bind()?
There's actually a very simple JavaScript rule that solves this. When you call a function as a method on an object, this automatically points to that object:
const person = { name: 'John' };
person.sayHi = function () {
console.log(this.name); // "John"
};
person.sayHi(); // this = person, because we called it as person.sayHi()That's the whole trick. If we temporarily attach our function to the context object and call it as a method, JavaScript gives us the right this automatically. After we're done, we clean up by deleting the temporary property.
Why Symbol?
We need to temporarily add the function as a property on the context object. But we need a property name that definitely doesn't already exist on that object. If we used a string like '__temp__' and the context object happened to already have a __temp__ property, we'd overwrite their data.
Symbol() creates a completely new, one-of-a-kind value every time you call it. No two Symbols are ever equal. Think of it like generating a random ID that's guaranteed to never collide with anything.
const a = Symbol();
const b = Symbol();
console.log(a === b); // false, alwaysSo context[Symbol()] = this adds our function under a key that cannot possibly conflict with any existing property. After we're done, we delete it to leave the object exactly as we found it.
Walking through the example
greet.myCall(person, 'Hello');thisinsidemyCallisgreet(because we calledgreet.myCall(...))context = person,args = ['Hello']- We create a Symbol and attach greet to person:
person[Symbol()] = greet - We call
person[Symbol()]('Hello'). Since we're calling it as a method onperson,thisinsidegreetis nowperson greetruns:`Hello, ${this.name}!`becomes"Hello, John!"- We delete the temporary property and return
"Hello, John!"
.call() executes the function immediately and returns
its result. .bind() does something different: it saves
the context and returns a new function for later. If
you're building all three polyfills, start with .call()
since it's the foundation. .apply() is nearly identical
to .call(), and .bind() uses .apply() internally.