.reduce() Polyfill
Published: 2022-01-09 15:48:24

> The full source code for the polyfill is available an the end ⬇️

## What is a polyfill?
A polyfill is a piece of code used to provide modern functionality on older browsers that do not natively support it.

## Polyfill for Array.prototype.reduce()
We will be writing the polyfill in steps, and in each step we will cover a `test case`.


> Note: In first 3 cases we will handle the error conditions

### Case 1:  Check if `this` exists, reduce is not called on null or undefined
To write the polyfill, we will create a function called `myReduce` which will take two arguments which are, a `callBack` function and the `initialValue` namely.

```js
function myReduce(callback, initialValue) {...}
```

In the current case, `this` refers to the array on which our `myReduce` is called. 

For example, 
```js
[1, 2, 3].myReduce(...)
```
then, in the above example `this` refers to the array [1, 2, 3].

moving ahead, in the current case, we have to check if `this` exists and reduce is not called on null or undefined, and if it is, then throw a TypeError ☠️.

So, the code for it will look like:

```js
function myReduce(callback, initialValue) {
	if (this === null || this === undefined) {
		throw new TypeError(
			'Array.prototype.myReduce is called on null or undefined'
		);
	}
}
```

### Case 2: Check if `callback` is a function
To handle this case, we add another check for callback, and throw a TypeError if otherwise.

```js
function myReduce(callback, initialValue) {
	if (this === null || this === undefined) {
		throw new TypeError(
			'Array.prototype.myReduce is called on null or undefined'
		);
	}

	if (!callback || typeof callback !== 'function') { // check for callback to exist, and it should be a function
		throw new TypeError(`${callback} is not a function`);
	}
}
```
### Case 3: Reduce on empty array with no initialValue should throw error
To handle this case, we add another if check, and throw a TypeError if otherwise.

```js
function myReduce(callback, initialValue) {
	if (this === null || this === undefined) {
		throw new TypeError(
			'Array.prototype.myReduce is called on null or undefined'
		);
	}

	if (!callback || typeof callback !== 'function') {
		throw new TypeError(`${callback} is not a function`);
	}

    if (!this.length) { // i.e. if array is empty
		if (arguments.length < 2) { // i.e.  if there are less than two arguments, then that means `initialValue` is missing, since callback is a mandatory argument for myReduce function. 
			throw new TypeError('Reduce of empty array with no initial value');
		}
	}
}
```
> Note: Read more about [arguments](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) here

### Case 4: Invoked on empty array with initialValue, returns initialValue
For this, we add an else if block, and check if there are exactly 2 arguments

> Note: for the sake of readability, I will be only showing the the code under discussion in code snippets.

```js
function myReduce(callback, initialValue) {
	...
	...

    if (!this.length) { // i.e. if array is empty
		if (arguments.length < 2) { // i.e.  if there are less than two arguments
			throw new TypeError('Reduce of empty array with no initial value');
		} else if (arguments.length === 2) { // This check means that there are exactly 2 arguments, which will namely be -> `callback` and `initialValue`
			return initialValue; // so, in that case return initialValue
		}
	}
}
```
For example,
```js
function sum(a, b) {
    return a + b
}

[].myReduce(sum, 5) // here `sum` is the callback function and 5 is the initialValue.

// OUTPUT
5
```

### Case 5: Invoked with initialValue
We initialize 2 variables:
         `k` equal to 0
and,   `acc` equal to `initialValue` (acc is short for accumulator, because it will be used to accumulate the result)

`acc` is set to `initialValue` because we want our accumulated result to already have the `initialValue` in it.

> Note: You may be wondering why are we using the `var` keyword to declare variables (which is usually a discouraged practice in modern web development), well since the whole point of a polyfill is that it should run in old browsers that don't support latest features yet, hence we are using `var` which has been a part of JS since the start.

We also start a while loop, which runs until `k` is less than the array's length, and inside the while loop we update the `acc` variable to `callback(acc, this[k], k, this)` and increment `k`

Now, `callback(acc, this[k], k, this)` might seem complex at first, so let me break it down.

By definition, a callback function in reduce has to take 4 parameters: 

1. accumulator (acc)
2. currentValue (this[k] i.e. the current value of of the array under iteration)
3. currentIndex (k) 
4. array (`this`)




```js
function myReduce(callback, initialValue) {
	...
	...
	...

	var k = 0;
	var acc = initialValue;

	while (k < this.length) {
		
		acc = callback(acc, this[k], k, this);

		k++; // increment k
	}

	return acc;
}
```

For example,
```js
function sum(a, b) {
    return a + b
}

[1, 2, 3].myReduce(sum, 1) // here 1 is the initialValue.

// OUTPUT
7
```

### Case 6: Invoked without initialValue
For this case, we check if `arguments.length` is less than 2, because it means that `initialValue` has not been provided, and if it is less than two then we equate the accumulator to `this[k++]`, which means the first value of the array (this[0], since k = 0 initially). We also post-increment `k` by using `k++`, because we want the while loop to start from index 1 and not from index 0 since the accumulator already has the initialValue as the value at index 0 in the array.



```js
function myReduce(callback, initialValue) {
...
...
...

	var k = 0;
	var acc = initialValue;

    if(arguments.length < 2){
       acc = this[k++] // update `acc` and increment `k`
    }

    // rest of the code stays the same

	while (k < this.length) {
		
		acc = callback(acc, this[k], k, this);

		k++;
	}

	return acc;
}
```

For example,
```js
function sum(a, b) {
    return a + b
}

[1, 2, 3].myReduce(sum) // no initialValue provided.

// OUTPUT
6
```

--- 
## Complete code
```js
function myReduce(callback, initialValue) {
	if (this === null || this === undefined) {
		throw new TypeError(
			'Array.prototype.myReduce is called on null or undefined'
		);
	}

	if (!callback || typeof callback !== 'function') {
		throw new TypeError(`${callback} is not a function`);
	}

	if (!this.length) {
		if (arguments.length < 2) {
			throw new TypeError('Reduce of empty array with no initial value');
		} else if (arguments.length === 2) {
			return initialValue;
		}
	}

	var k = 0;
	var acc = arguments.length < 2 ? this[k++] : initialValue;

	while (k < this.length) {
		if (Object.prototype.hasOwnProperty.call(this, k)) {
			acc = callback(acc, this[k], k, this);
		}
		k++;
	}

	return acc;
}
```

---

This blogpost is inspired by  [this youtube video](https://www.youtube.com/watch?v=EgN4qwAQ0eM&ab_channel=DevtoolsTech) which does a great job at explanation.

--- 
Thankyou for reading this article! Let's become friends on   [twitter](https://twitter.com/vikrantbhat1022)🐤 also :)