DEV Community

Cover image for Implementing built-in JavaScript methods from scratch
Brian Neville-O'Neill
Brian Neville-O'Neill

Posted on • Originally published at blog.logrocket.com on

Implementing built-in JavaScript methods from scratch

Written by Akshar Takle✏️

Javascript is filled with numerous built-in methods that help accomplish a lot of tasks in just a line of code.

You might have used them in your project without knowing how they work under the hood. This post is about looking inside these functions.

I’ve seen many companies asking to implement these functions from scratch in their interviews, so that’s what we will do! We will take a bunch of inbuilt javascript functions that you use almost every day and implement them from scratch.

I believe doing this will also give you more confidence in using these functions like a pro.

LogRocket Free Trial Banner

Map

Good old map is a higher-order function. It iterates over the elements of a given array, applies a transform function on each element, adds the element to a new array, and returns the new array.

It is one of the most useful functions of the functional programming toolbox.

The important point to note about map is that it allows you to transform the entire list of values without modifying the original list.

So, this is how all the magic happens:

const Map = (array, fn) => {
 const answer = [];
 for (let i = 0; i < array.length; i++) {
   answer.push(fn(array[i]));
 }
 return answer;
};
Enter fullscreen mode Exit fullscreen mode

Reduce

Reduce is a very useful function when you have a list of values that you want to combine into a single value in a meaningful way.

The reduce function iterates overall values of the given array and returns a single value.

It does not return a new array like map. Reduce outputs a single value which can be a number, string or an object.

Let’s see how reduce works in action:

const Reduce = (list, fn, seed) => {
 let result = seed;
 for (let i = 0; i < list.length; i++) {
   result = fn(answer, list[i]);
 }
 return result;
};
Enter fullscreen mode Exit fullscreen mode

So, reduce involves a list upon which it is called, a reducing function, an accumulator and a seed value.

The accumulator is a temporary / interim result that holds the value returned by the reducer function. The returned value is again passed on to the next reducer functions that runs on the next value in the array.

The seed value is the first value of the accumulator.

If no seed value is passed, the first element in the list is taken as seed.

const list = [1,2,3];
list.reduce(function(accumulator, number) {
   return accumulator + number;
});
// returns 6 since 1 becomes the seed
Enter fullscreen mode Exit fullscreen mode

Filter

Filter does exactly what its name sounds like. It returns a new array of elements filtered from the original array.

We just need to write a function that returns true if we want to keep the current item in the list, or returns false if not.

const Filter = (list, fn) => {
 const result = [];
 for (let i = 0; i < list.length; i++) {
   if (fn(list[i])) {
     result.push(list[i]);
   }
 }
 return result;
};
Enter fullscreen mode Exit fullscreen mode

Here is how we can use it to filter out all the odd numbers in the given array:

const filterOddOnesOut = nums => nums.filter( num => num % 2 ===
Enter fullscreen mode Exit fullscreen mode

Debounce

If you have ever thought of implementing autocomplete or typehead, you’ve probably used debounce. It’s a way of throttling the number of network calls fired when the user is typing.

Let’s implement this from scratch:

const debounce = (fn, time) => {
 let setTimeoutId;

 return function() {
     if(setTimeoutId) {
       clearTimeout(setTimeoutId);
     }

     setTimeoutId = setTimeout(() => {
       fn.apply(this, arguments);
       setTimeoutId = null;
     }, time);
 }
}
Enter fullscreen mode Exit fullscreen mode

So now, as the user is typing, let’s say we call the debounce function in a row:

debounce(someFunction, 500);
debounce(someFunction, 500);
debounce(someFunction, 500);
Enter fullscreen mode Exit fullscreen mode

Only the last one will ever get executed because clearTimeout will cancel the previous ones if the new one happens to be called before the timeout.

Bind

With JavaScript, we often need to interact with the scope, especially when we’re using React.

The scope is essentially the context we are operating in and all the things that are available to us. Generally, functions like call and apply are used to change the current execution scope in JavaScript.

Both of these methods not only change the scope, but also execute the given function immediately. With bind, we still change the scope but return a function that can be called later.

Let’s see how to write bind from scratch.

We will use the call method to implement this:

const bind = (fn, context) => {
    return function () {
       fn.call(context);
    }
}
Enter fullscreen mode Exit fullscreen mode

Sort

The sort function returns a sorted array from the given array. Let’s see how sort works under the hood.

We will use a merge sort algorithm for this. When we call Array.prototype.sort, it often uses merge sort in the background.

Merge sort is a divide and conquer algorithm. In this algorithm, we basically take a list, divide it into two halves, and call merge sort on them recursively which in turn does the same.

The base case is when we have a list of just one element. In that case, we just return that list back.

As you walk your way up through the recursive calls, we merge the two sorted lists together:

const mergeSort = list => {
// base case
 if (list.length < 2) {
   return list;
 }
 const length = list.length;
 const middle = Math.floor(length / 2);
 const left = list.slice(0, middle);
 const right = list.slice(middle);

 return merge(mergeSort(left), mergeSort(right));
};
const merge = (left, right) => {

 const results = [];

 while (left.length && right.length) {

   if (left[0] <= right[0]) {
     results.push(left.shift());
   }
   else {
     results.push(right.shift());
   }
 }

 return results.concat(left, right);
};
Enter fullscreen mode Exit fullscreen mode

As you can see, we have a merge function that goes through both the left and right list and inserts the smaller value first, resulting in a bigger sorted list.

Conclusion

All these built in JavaScript methods are really powerful. By reimplementing them from scratch, we are in a better position to use them effectively.


Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

Alt Text

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — start monitoring for free.


The post Implementing built-in JavaScript methods from scratch appeared first on LogRocket Blog.

Top comments (0)