DEV Community

AR Addula
AR Addula

Posted on • Edited on

JS Polyfills - Call, Apply, Bind

If you are here, I guess you would either be looking for new opportunities or honing your skills. I would like to help for either reasons with this article. Let's directly jump in to the topic without wasting our read time 🤘

Polyfill for Call

//runCall has no defined parameters
function runCall () {
arguments[0].fn = this;
//converting arguments to proper array
let paramsArray = [];
for(let arg of arguments) {
paramsArray.push(arg);
}
arguments[0].fn(...paramsArray.slice(1));
//OR
//arguments[0].fn.apply(arguments[0], paramsArray.slice(1));
}
//runCall2 has defined parameters
function runCall2 (context, ...args) {
context.fn = this;
context.fn(...args);
//OR
//context.fn.apply(context, args);
}
Function.prototype.runCall = runCall;
Function.prototype.runCall2 = runCall2;
function displayUser (state, country, method) {
console.log('----- '+method+' -----');
console.log('Name : ', this.name);
console.log('Age : ', this.age);
console.log('City : ', this.city);
console.log('State : ', state);
console.log('Country : ', country);
}
var user = {
name: 'John Stewart',
age: '🙊',
city: 'Sanfrancisco'
}
displayUser.runCall(user, 'CA', 'USA', 'call --> runCall'); //----> passing context, params
displayUser.runCall2(user, 'CA', 'USA', 'call --> runCall'); //----> passing context, params

Explanation:

What exactly is this 🧐 -> arguments[0].fn = this;

In this above statement this refers to calling function i.e; displayUser

  • We are creating a property(fn) on object and assigning displayUser function to it
  • Above would create an additional property fn on the passed object(user)
  • Now, fn would have access to this on user object as fn is user objects property now
  • So, pass only parameters now to function fn now, as it takes care of this
  • Both runCall and runCall2 are valid polyfills for call where runCall2 has defined parameters and runCall depends on arguments object
  • Look at the code below and compare with lines written inside function runCall
var user = {
name: 'John Stewart',
age: '🙊',
city: 'Sanfrancisco'
}
var funcThis = function (state, country, method) {
console.log('----- '+method+' -----');
console.log('Name : ', this.name);
console.log('Age : ', this.age);
console.log('City : ', this.city);
console.log('State : ', state);
console.log('Country : ', country);
}
user.fn = funcThis; //What happens here is the JS Magic! Checkout Objects, Prototypes on MDN for better understanding
user.fn("CA", "USA", "Object Attaching");

Polyfill for Apply

Apply polyfill is similar to call except that apply takes arguments as Array along with object(context)

function runApply () {
arguments[0].fn = this;
arguments[0].fn(...arguments[1]);
}
Function.prototype.runApply = runApply;
function displayUser (state, country, method) {
console.log('----- '+method+' -----');
console.log('Name : ', this.name);
console.log('Age : ', this.age);
console.log('City : ', this.city);
console.log('State : ', state);
console.log('Country : ', country);
}
var user = {
name: 'John Stewart',
age: '🙊',
city: 'Sanfrancisco'
}
displayUser.runApply(user, ['CA', 'USA', 'apply --> runApply']);

Polyfill for Bind

Here comes the solution for our wait, atleast I feel though 😉

function es5Bind () {
//arguments are just Array-like but not actual Array. Check MDN.
let bindFn = this,
bindObj = arguments[0],
bindParams = [].slice.call(arguments,1); //----> [arg1,arg2..] Array.isArray --> true
return function () {
bindFn.apply(bindObj, bindParams.concat([].slice.call(arguments)))
}
}
function es6Bind(...bindArgs) {
let context = this;
return function (...funcArgs) {
context.call(bindArgs[0], ...[...(bindArgs.slice(1)), ...funcArgs]);
// we can use above line using call (OR) below line using apply
//context.apply(bindArgs[0], [...(bindArgs.slice(1)), ...funcArgs]);
}
}
Function.prototype.es5Bind = es5Bind;
Function.prototype.es6Bind = es6Bind;
function displayUser (state, country, method) {
console.log('----- '+method+' -----');
console.log('Name : ', this.name);
console.log('Age : ', this.age);
console.log('City : ', this.city);
console.log('State : ', state);
console.log('Country : ', country);
}
var user = {
name: 'John Stewart',
age: '🙊',
city: 'Sanfrancisco'
}
let myDetails = displayUser.es5Bind(user, 'CA'); //----> passing context, params
myDetails('USA', 'bind --> es5Bind'); //----> passing function params
myDetails = displayUser.es6Bind(user, 'CA');
myDetails('USA', 'bind --> es6Bind');

There are two functions created for bind polyfill in the above code. One is using [].slice.call(arguments) and the other one is using rest/spread syntax ...arguments

Thank you for taking time to read my article. I know there would always be something to improve. Please feel free to share your thoughts 👐

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (2)

Collapse
 
ivasilichi profile image
Oleg Vaskevich

arguments[0].fn(...[].slice.call(arguments,1)); You implementing call polyfill using call, how is that even polyfill?

Collapse
 
araddula profile image
AR Addula

@ivasilichi Thanks for pointing this out. I was more concentrating on "bind" polyfill and overlooked "call". I was also not active on this forum so far. It is corrected/updated now. Thanks again

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs