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 tothis
on user object asfn
is user objects property now - So, pass only parameters now to function
fn
now, as it takes care ofthis
- Both
runCall
andrunCall2
are valid polyfills forcall
whererunCall2
has defined parameters andrunCall
depends onarguments
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 👐
Top comments (2)
arguments[0].fn(...[].slice.call(arguments,1)); You implementing call polyfill using call, how is that even polyfill?
@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