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.
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;
};
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;
};
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
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;
};
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 ===
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);
}
}
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);
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.
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);
}
}
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);
};
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.
All these built in JavaScript methods are really powerful. By reimplementing them from scratch, we are in a better position to use them effectively.
Originally published by Akshar Takle at https://blog.logrocket.com
#javascript #web-development