Today's Curious topic: How `.call()` work and how can we write a custom `.myCall()` in javascript

Today's Curious topic: How .call()
work and how can we write a custom .myCall()
in javascript
Inside Web Cohort 1.0 class Piyush Garg Share exciting and wonderful tasks for us like writing custom polyfill of .call()
, .bind()
, and .apply()
Before we start I would like to share some what if:
What if we can able to make function calls without directly using
<functionName>(…arguments)
?What if we can able to share and pass extra context while calling function runtime?
Knowledge base
Reference example from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
Here we have two function Product
and Food
Now I want to call the Product
function from Food
, but we would be curious why
- ? :
Name
andPrice
properties are the same asProduct
function, then why should be writing the second time
Before we go ahead let's see some code:
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
// ohh wow we can all call the function like this to 😲😲
Product.call(this, name, price);
this.category = "food";
}
console.log(new Food("cheese", 5).name);
// Expected output: "cheese"
Now .call()
function should be very easy to understand, why we should use it
🏗 Start Custom Building
There are a few hurdles before starting like:
How can we attach
myCall
function globally so if I create any function then I can able to access itHow can we able to make a Function call runtime?
How can we share runtime context or value to a function?
How Can we attach .myCall
function globally
- while searching found that there was one global
Function
Object which we can able to use but there was one more point we didn’t know like the below code didn’t work
Function.myCall = function (){
console.log('Hello world of dynamic function calling');
}
function Product(name, price){
this.name = name;
this.price = price;
}
function Food(name, price) {
// ! it didn't work I didn't able to find myCall here
// ? Error like: TypeErro myCall is not a function
Product.myCall(this, name, price);
this.category = "food";
}
Then after some learning and researching found that there was one parent property, which we can able to accessed and able to add value inside them, which would reflect in all instances
Function.prototype.myCall = function (){
console.log('Hello world of dynamic function calling');
}
function Product(name, price){
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.myCall(); // Output: Hello world of dynamic function calling
this.category = "food";
}
Hurdle 1 complete
How can we able to make a Function call runtime?
while googling and finding ways to do found some ideas like below in that we can able to found one way to make function call runtime without extra argument passing
Function.prototype.myCall = function (func) {
// 1. this would work but we have to manually add `Product` as argument then it's not good write
// func();
// 2. can we access function name with `this` ?
console.log(this.name) // Output: Product
// 3. can we direct call function with `this` ?
// this(); Error: this would not work syntax error
// 4. what if we can store inside variable and able to store it
const func = this;
func('cheese', 10); // Output: {name: cheese, price: 10}
};
function Product(name, price) {
this.name = name;
this.price = price;
console.log({name, price});
}
Product.myCall(Product);
How can we share runtime context or value to a function?
Let’s see one diagram which helps to solve our issue
so if we have a common context then we can share dynamic context and values as well, then let’s try inside code as well
concept learning and ideas from: https://stackoverflow.com/questions/70734258/polyfill-for-bind-includes-using-call-or-apply-why
// check if already myCall was declared then we didn't want to do anything else we want to define
if (typeof Function?.myCall === "undefined") {
// attaching myCall inside Function object parent property
/**
* @param {*} context // dynamic context we can share
* @param {...any} args // all dynamic argument we shared runtime
*/
Function.prototype.myCall = function (context, ...args) {
// creating wrapper with current context if it's not available then we are using custom object
const wrapperParentObject = context || {};
// let's add current context which was myFunction ex: `Product` into parentWrapperObject
wrapperParentObject.myFunc = this;
// checking if we are any runtime arguments then we are calling function with that
// as we already have common parent wrapper we have dynamic context while calling function
if (args?.length > 0) wrapperParentObject.myFunc(...args);
// if we didn't have any arguments then we are only calling function
else wrapperParentObject.myFunc();
// delete dynamic function added inside wrapper parent object
delete wrapperParentObject.myFunc;
};
}
then the code that was present inside the mdn example would all work with a custom .myCall
? (Yes it will work)
if (typeof Function?.myCall === "undefined") {
// attaching myCall inside Function object parent property
/**
* @param {*} context // dynamic context we can share
* @param {...any} args // all dynamic argument we shared runtime
*/
Function.prototype.myCall = function (context, ...args) {
// creating wrapper with current context if it's not available then we are using custom object
const wrapperParentObject = context || {};
// let's add current context which was myFunction ex: `Product` into parentWrapperObject
wrapperParentObject.myFunc = this;
// checking if we are any runtime arguments then we are calling function with that
// as we already have common parent wrapper we have dynamic context while calling function
if (args?.length > 0) wrapperParentObject.myFunc(...args);
// if we didn't have any arguments then we are only calling function
else wrapperParentObject.myFunc();
// delete dynamic function added inside wrapper parent object
delete wrapperParentObject.myFunc;
};
}
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.myCall(this, name, price);
this.category = "food";
}
console.log(new Food("cheese", 5).name);
// Expected output: "cheese"
// Original output: "cheese"
if we can pass dynamic argument then we can all able to create our own custom
.myApply
if we are storing
myFunc
insideparentWrapperObject
what if we return that function without calling- then I think it would work like a custom
.myBind
function
- then I think it would work like a custom
Things we learn together
The biggest topic that we learn like polyfill
understand working on
.call()
experiment with a dynamic function call
understand like parent property
prototype
(as per I know: it’s only present in pre-defined instances not before that)understand basic of
this
keyword work and usageif we want to share a common context then we should wrap it with one object which helps to solve the issue
I’m Thankful to Hitesh Choudhary Sir and Piyush Garg Sir for sharing this wonderful task with us while learning
Subscribe to my newsletter
Read articles from Suchit Sheth directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
