DEV Community

Mastering Hard Parts of JavaScript: Callbacks I

Ryan Ameri on August 08, 2020

I'm currently undertaking JavaScript: The Hard Parts v2 course at Frontend Masters. It is a brilliant course taught by the amazing Will Sentance. T...
Collapse
 
jmatty1983 profile image
Jason Matthews • Edited

Being that this is intended to teach "the hard parts of JavaScript" I think you should take some care to not teach bad habits inadvertently as well.

Specifically exercise 6. You're reusing initialValue as your accumulator. Firstly, unless you're doing some kind of crazy micro-optimization, just don't ever reuse function inputs in your functions. Ever. You will undoubtedly create unintended side effects that waste countless hours debugging.

Consider the case where the array is not an array of primitives. You assign the initialValue to array[0] if an initialValue wasn't defined. Seems innocent enough. In this case though, it's a reference and not a copy. Now on each iteration of the forEach you're modifying the first element in the input array! Surely that's not what's intended and, especially for your target audience here, you're going to create a very frustrating future experience for them.

Now also consider the case where initial value is defined but is not a primitive value. If I was inputting something I wanted to use later in my code it would be unexpectedly changed!

Instead you should take care to create a copy of array[0] and initialValue when setting the value for your accumulator.

Collapse
 
internettradie profile image
Ryan Ameri

You're 100% correct there. I should have assigned initialValue to a new variable to make sure the function has no side effects. Bad miss on my part. Thanks for pointing out 😊

Collapse
 
shanwshaw profile image
shawnshaw • Edited

maybe then update the post as not everyone is going to fully read the comments section :)

Thread Thread
 
internettradie profile image
Ryan Ameri

Done!

Collapse
 
jkoo92 profile image
JKoo92

Hey Ryan thank you for this post that I just came across. Do you have your previous solution by any chance so that I can better understand what Jason is pointing out in the comment? Is he saying that in your previous solution you just set " let accum = initialValue" ?

Collapse
 
semigradsky profile image
Dmitry Semigradsky

Current reduce implementation is wrong - it will handle first item twice if you will not set initialValue.

Collapse
 
internettradie profile image
Ryan Ameri

Thanks for picking that up! Should be fixed now 😀

Collapse
 
semigradsky profile image
Dmitry Semigradsky

if (Object.keys(arguments).length > 2) {

I think you can just use arguments.length. Cheers!

Thread Thread
 
deryckhenson profile image
Deryck Henson

better yet, typeof initialValue !== 'undefined' so you avoid touching arguments at all

Thread Thread
 
semigradsky profile image
Dmitry Semigradsky

initialValue can be undefined

Thread Thread
 
deryckhenson profile image
Deryck Henson

yes...but accum = initialValue loses its purpose if it is.

Collapse
 
flippsn profile image
Flippsn • Edited

Hi Ryan! I'm pretty new on my learning journey for JavaScript and would have a question regarding exercise 5.

I generally do get the structure of callbacks, but there is one thing I simply cannot wrap my head around:
Why does the anonymous function within the forEach function require the "item" parameter (see below on line 3).

function mapWith(array, callback) {
const newArr = [];
forEach(array, (item) => {
newArr.push(callback(item));
});
return newArr;
}

Since forEach(array, callback) does execute "callback(array[i])" I simply cannot wrap my head around why this solution would not work:

function mapWith(array, callback) {
let arrNew = [];
forEach(array, () => {
arrNew.push(callback());
});
return arrNew;
}

Maybe you could provide some additional insights? Thank you!

Collapse
 
mehran1801 profile image
Mehran Khan • Edited

Q: " Since forEach(array, callback) does execute "callback(array[i])" I simply cannot wrap my head around why this solution would not work: "

Answer:
The reason it would not work is because callback(array[i]) will not push the resulting element to the output array, as a result you will get 'undefined' because it hasn't pushed the results into the resulting array. It would simply apply the callback on each item/element. so what we need to do is define another callback method in forEach function.

for(let i=0; i<array.length; i++){
output.push(callback(array[i]))

Basically we want to wrap up this entire functionality into callback of forEach function.

Q: " Why does the anonymous function within the forEach function require the "item" parameter"

forEach(array, (item) => {
newArr.push(callback(item));
});

So for every item that we pass in we want it to push the result of calling the callback with that item into the resulting array. so let's say we have array [1,2,3] the item parameter would call 1 first and apply the callback and push to the resulting array and so on...

There are multiple arguments being passed into multiple parameters and it is kind of confusing but if you step through it line by line using console log statements, it will eventually make sense.
Happy Coding!