0

I do not understand how the parameter of the first class function, "number", has the value it does.

I have been mulling over this problem for a couple of days and I am not making any progress in my thinking. There is no error code as this is perfectly valid JS.

This example code is from the book Eloquent JS (http://eloquentjavascript.net/2nd_edition/05_higher_order.html#c_chCFkdNvRH 2nd ed).

// definition
function forEach(array, action) {
  for (var i = 0; i < array.length; i++)
    action(array[i]);
}

//vars
var numbers = [1, 2, 3, 4, 5], sum = 0;

// implemented function
forEach(numbers, function(number) {
  sum += number;
});

//output
console.log(sum);
// → 15

I dont understand why 'number' is not considered undefined. Shouldn't the parameter be 'numbers[i]' or 'numbers', which references the actual array?

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
sector7g
  • 5
  • 3

4 Answers4

1

In the forEach declaration you are passing a function reference as a second argument which you are calling and passing a value from the array:

function forEach(array, action) <--- action is a function reference

action is called like action(array[i]), with a value from array.

Now when forEach is called the value for the action is an actual function object.

You can visualize it as such:

action = function(number) {
  sum += number;
}

action is expected to take a argument which is called with a value from an array, so the number parameter is actually that value from the array array[i]

Fullstack Guy
  • 16,368
  • 3
  • 29
  • 44
0

If we want to unravel what this code is doing, we can rewrite it without using a function that accepts another function as a parameter. This process of unraveling is called inlining. Here, we are inlining forEach():

// vars
var numbers = [1, 2, 3, 4, 5], sum = 0;

// inlined forEach
var array = numbers;
var action = function(number) {
  sum += number;
};

for (var i = 0; i < array.length; i++)
  action(array[i]);

// output
console.log(sum);
// → 15

We can unravel this code again by inlining action():

// vars
var numbers = [1, 2, 3, 4, 5], sum = 0;

var array = numbers;

for (var i = 0; i < array.length; i++) {
  // inlined action
  var number = array[i];
  sum += number;
}

// output
console.log(sum);
// → 15

So in fact what's happening is we assign numbers to array and

function(number) {
  sum += number;
}

to action, then we assign array[i] to number when we call action(array[i]), which means the following code is equivalent, without using the extra variables:

// vars
var numbers = [1, 2, 3, 4, 5], sum = 0;

for (var i = 0; i < numbers.length; i++)
  sum += numbers[i];

// output
console.log(sum);
// → 15

Of course, the point of forEach() is to avoid having to rewrite all of that logic each time we need to loop through an array and do something with each item.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • Thank you for the indepth comment but I think that it is most fair to award based on the first answer that sufficiently addressed my question. However, based on your comment I now just gleaned a really great approach and I will be using inlining to splay all my future questions. Thanks again for your time. – sector7g Aug 08 '19 at 20:53
0

Here,

function forEach(array, action) {
  for (var i = 0; i < array.length; i++)
    action(array[i]);
}

You define a function, okay and it has two arguments, one is an array and another is a callback. The argument action is a callback when you add parentheses around it action(data) and this data is sent to the callback function because you added parentheses around it.

forEach(numbers, function(number) { // here the number variable is array[i]
  sum += number; // now sum = 0 then you are doing the array[i] + the last value into the sum variable.
});

Hence you get sum as the sum of the array values.

To understand callback, take this basic snippet

function hello(text,callback){
  callback(text,", World") // the first arg is text and the second is a string as "World"
}
hello("Hello", function(text,worldtext){
  console.log(text + worldtext) // Hello, world because we did text + worldtext
});

It takes the first argument and a callback where we can use to access the variable and do things with it.

weegee
  • 3,256
  • 2
  • 18
  • 32
0

The basic summary is that there is a difference between a parameter and an argument.

Let's look at a function definition:

function addOne(amount) {
    console.log(amount + 1);
}

In this function, amount is a parameter, not an argument. The parameter is essentially just the variable name that will be referenced inside this function; it has no value on its own.

But then, we can call this function:

addOne(4);

Here, the argument is 4. The parameter is still amount. What happens is the argument 4 is bound to the parameter amount -- that is, amount "takes on" the value of 4 during this particular execution of the function.

So in your code, you're defining a function as follows:

function(number) {
  sum += number;
};

This function has no name, and it has a parameter: number. Only when the function is called (which it hasn't been yet) will number have a value. But luckily, you're passing this function on to forEach, which DOES call it with a value:

action(array[i]);

(Remember: in the forEach function, action is a parameter, and since you've called forEach with our anonymous function above as the argument, action will reference that function during this execution.)

Since the argument here is array[i], and it corresponds to the anonymous function's parameter number, during this execution, the value of number will be set to the value of array[i]

I hope that clears things up for you :)

IceMetalPunk
  • 5,476
  • 3
  • 19
  • 26