DEV Community

Devyank Nagpal
Devyank Nagpal

Posted on

polyFills in javascript

Note: These polyfills were written to help me (and others) understand how JavaScript methods work internally. They’re not meant to be exact replacements for the native implementations.

Map, Filter and Reduce

Map

  • The map function accepts a single callback function as its argument.

  • We use a temporary array to store the new results.

  • The callback function receives three arguments — the current element, the current index, and the array itself.

  • Inside the callback, this refers to the array on which the map function is being called.

Array.prototype.devMap=function(cb){   

let arr=[]; 
for(let i=0;i<this.length;i++){
arr.push(cb(this[i],i,this)); 
}
return arr;
Enter fullscreen mode Exit fullscreen mode

Filter

  • The filter function accepts a single callback function as its argument.

  • Similar to the map function, we use a temporary array to store the filtered results.

  • Inside the callback, if the specified condition is met, the corresponding value is added to the new array.

Array.prototype.devFilter=function(cb){
  let temp=[];
  for(let i=0;i<this.length;i++){
    if(cb(this[i],i,this)){  
      temp.push(this[i]);
    }
  }
  return temp;
}
Enter fullscreen mode Exit fullscreen mode

Reduce

  • Polyfill for the reduce function in JavaScript

  • The reduce function accepts a callback function and an initial value.

  • By default, the initial value (often referred to as iv) is assigned to the accumulator.

  • The callback function receives four arguments — the accumulator, the current element, the current index, and the array itself.

  • If the initial value is not provided, the first element of the array is used as the accumulator.

  • The results returned from each callback execution are then stored in the accumulator as the reduction progresses.

Array.prototype.devReduce=function(cb,iv) //iv-> initialValue
  let acc=iv; 
  for(let i=0;i<this.length;i++){
    acc=acc?cb(acc,this[i],i,this):this[i]; //this->refers to array 
  }
return acc;

}
Enter fullscreen mode Exit fullscreen mode

Call ,Apply ,Bind

Call

Before exploring the call prototype function, let’s first understand its use case.
Suppose there is a function named getName and an object student that contains a key-value pair for name.
If we want to use the name value from the student object inside the getName function, we can achieve this by calling the function as getName.call(student).
When using call, the this keyword inside the getName function refers to the student object.
Without using call, this inside the getName function would refer to the global object.

 function getName(){
 console.log(`this function print name ${this.name}`};
 }
 const student={
 name:"devyank"
 }
 getName.call(student);
Enter fullscreen mode Exit fullscreen mode

Now, to create a polyfill for the call function, we need to understand that call accepts an object and additional arguments.
Since inside the function we can access the keys or properties of the provided object, we need to temporarily make that function a part of the object itself.

Polyfill for call

Function.prototype.devCall=function(ob,...args){
ob.fn=this;
ob.fn(...args);
}
Enter fullscreen mode Exit fullscreen mode

Apply

The apply method is similar to call; the only difference is that the arguments are passed in the form of an array.
For example, in the example below, we have passed 15 as an age argument.

function getName(age){
 console.log(`this function print name ${this.name} and ${age}`};
 }
 const student={
 name:"devyank"
 }
 getName.call(student,15); 

Enter fullscreen mode Exit fullscreen mode

Polyfill for Apply

Function.prototype.devApply=function(ob,args=[]){
ob.fn=this;
ob.fn(...args);
}
Enter fullscreen mode Exit fullscreen mode

Bind

This function is quite different from call and apply, as it returns a new function. However, its behavior is similar to that of the call function.

function getName(age){
  console.log(`this function print name ${this.name} and ${age}`);
}

const student={
  name:"devyank"
}
const bindFunc=getName.bind(student);
bindFunc(15);

Enter fullscreen mode Exit fullscreen mode

Hence, for the bind polyfill as well, we assign the function to the object so that we can access its properties inside the function.
The only difference is that, in this case, we return a new function using bind instead of invoking it immediately.

Polyfill for Bind

Function.prototype.devBind=function(ob,...args){
ob.fn=this;
return function(...myargs){
  return ob.fn(...args,...myargs);
}

}
Enter fullscreen mode Exit fullscreen mode

Polyfill for Promise

To create a Promise polyfill, we first need to understand that a Promise has two internal functions — resolve and reject — and its instance provides two methods — then and catch.

There are two cases to consider: synchronous and asynchronous execution.
In the case of an asynchronous function, the value of onResolve is already a function. Therefore, inside the resolve function, we check whether onResolve is a function before calling it.

The callback functions passed to then and catch are assigned to onResolve and onReject, respectively.
In synchronous scenarios, onResolve and onReject are not yet functions when resolve or reject are called, so they are executed later inside the then and catch methods.
However, in asynchronous scenarios, the then and catch methods are executed before resolve or reject are triggered. As a result, the callbacks are already assigned to onResolve and onReject, allowing them to be called directly inside the resolve and reject functions.

function promisePolyfill(executor){
  let onResolve,onReject,isFullfilled,isRejected,isCalled,value;
  function resolve(val){
    value=val;
    isFullfilled=true;
    if(typeof onResolve==='function'){
      onResolve(val);
      isCalled=true;

    }
  }
  function reject(val){
    value=val;
    isRejected=true;
    if(typeof onReject==='function'){
      onReject(val);
      isCalled=true;

    }
  }
  this.then=function(cb){
    onResolve=cb;
    if(isFullfilled && !isCalled){
      onResolve(value);
      isCalled=true;
    }
    return this;
  }
  this.catch=function(cb){
    onReject=cb;
    if(isRejected && !isCalled){
      onReject(value);
      isCalled=true;
    }
    return this;
  }
  executor(resolve,reject);

} 

Enter fullscreen mode Exit fullscreen mode

Polyfills for Debouncing and Throttle

Debouncing

This function accepts a callback function and a delay time.
We maintain a variable timer to store the result returned by the setTimeout function.
The callback is executed after the specified delay.
If a timer already exists, we clear the previous timeout before setting a new one.

function useDebounce(fn,delay){
  let timer;
  return function(...args){
    if(timer) clearTimeout(timer);
    timer=setTimeout(()=>{
      fn(...args);
    },delay);
  }
}
Enter fullscreen mode Exit fullscreen mode

Throttling

In this approach, we maintain a variable last to store the last execution time.
We then check if the difference between the current time and the last execution time is smaller than the specified delay.
If it is, we simply return without calling the function; otherwise, we allow the function to execute.

function useThrottle(fn,delay){
  let last;
  return function(...args){
    let now=new Date().getTime();
    if(now-last<delay)return;
    last=now;
    return fn(...args);
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (3)

Collapse
 
jonrandy profile image
Jon Randy 🎖️

Unfortunately, none of your array polyfills are valid polyfills for their native counterparts. The first two (map, filter) are missing the optional thisArg paramater, and none of them deal with sparse arrays correctly.

Collapse
 
2devyank profile image
Devyank Nagpal

Great catch — you’re absolutely right! These weren’t meant to be full spec-compliant polyfills, but rather a simplified version I built for learning and understanding the core logic behind methods like map and filter.
Still, I really appreciate the clarification about thisArg and sparse array handling — those are important details for a truly accurate polyfill. Thanks for taking the time to share your insight!

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

Then you're just adding similar methods, NOT polyfills. A polyfill is used to exactly duplicate functionality that is missing or unavailable in a specific environment for whatever reason.