How does a function gets passed in to another function? - javascript

I have a forEach function defined to do "something" with all the items in an array:
function forEach(array, action) {
for (var i = 0; i < array.length; i++)
action(array[i]);
}
var numbers = [1, 2, 3, 4, 5], sum = 0;
So I could do:
forEach(numbers, console.log);
And it would print out all the numbers because 'console.log(array[i])' prints the number to the console.
I get that part.
Here's where I'm stuck: If I pass the function below into the place of the action parameter, instead of 'console.log' then at what point does the function know about every element?
forEach(numbers, function(number) {
sum += number;
});
console.log(sum);
// 15
How does it get evaluated? If I pass console.log into the last problem and it still has to be evaluated as 'console.log(array[i])' with the '(array[i])' code next to whatever parameter is being passed in, then doesn't that get applied to the entire function too since that function is the parameter? Such as below:
function(number) { sum += number; }(array[i])

at what point does the function know about every element
At no point (just like when you pass in console.log).
The forEach function calls it on this line:
action(array[i]);
At which point, it only knows about a single value from the array because that is all that is passed into it.
(It also knows about the sum variable because that is defined in a wider scope than the function).

How does it get evaluated?
It creates a new scope (with array, action and i variables) and assigns the function to the action variable - that's a function invocation.
Your
var sum = 0;
forEach([1, 2, 3, 4, 5], function(number) {
sum += number;
});
is just the same as
var sum = 0;
{ // let's assume block scope here
var array = [1, 2, 3, 4, 5],
action = function(number) {
sum += number;
},
i;
for (i = 0; i < array.length; i++)
action(array[i]);
}
If I pass console.log into the last problem and it still has to be evaluated, then doesn't that apply to the entire function too since that function is the parameter?
Yes, exactly. You are passing a function object - and whether get that by referencing the console.log variable or by creating it on the fly (with the function expression) doesn't matter to forEach. It only will get executed with action(…) - where the array[i] value is passed for the number parameter of your function.

In JavaScript, functions are first-class citizens. That means they can be treated as variables, just like strings, numbers, etc.
When you do:
forEach(numbers, function(number) {
sum += number;
});
You are passing forEach an anonymous function. Instead of the function being in a variable, it's created on-the-fly. Inside your forEach function, action will contain your anonymous function.
In for for loop, the action function is called for each element.

Heres the solution to your problems:
function forEach(array, action) {
for (var i = 0; i < array.length; i++){
if(typeof action == 'function'){
action(array[i]);
}else{
var slices = action.match(/(.+)\.([a-zA-Z0-9]+)/);
var object = eval(slices[1]);
var action = slices[2];
object[action](array[i]);
}
}
}
I've tested it with both scenarios and it works like magic

Related

Where does a function receive its input when passing the function as an argument?

I have the following code:
function forEach(array, action) {
for (var i = 0; i < array.length; i++)
action(array[i]);
}
var numbers = [1, 2, 3, 4, 5], sum = 0;
forEach(numbers, function(number) {
sum += number;
});
console.log(sum);
Where does the function(number){ sum += number; } receive its arguments when passed? Does the resulting function look like this?
function(array[i]){sum += array[i]}
The normal way: Between the parenthesis when it gets called.
action(array[i]);
^^^^^^^^
Where does the function(number){ sum += number; } receive its arguments when passed?
You have created a function expression and passed it as an argument to the function (forEach). That function has one argument. It could be used while calling it.
action(array[i]);
//array[i] will be received as an argument to the passed function expression
Inside forEach function action would be a function reference. And that function reference is capable of passing one known parameter. So you can call it in anyways you want.

Javascript syntax with functions

I'm not sure what this means or how to word the question correctly.
In the first example I could put the variable before the function "numbers1.forEach(...)" but in the second example I could not "num.square()"?
function forEach(array, action) {
for (var i = 0; i < array.length; i++)
action(array[i]);
}
var numbers2 = [1, 2, 3, 4, 5], sum = 0;
forEach(numbers2, function(number) {
sum += number;
});
console.log(sum);
// → 15
var numbers1 = [1, 2, 3, 4, 5], sum = 0;
numbers1.forEach(function(number) {
sum += number;
});
console.log(sum);
// → 15
// this works!
But this does not work
var square = function(x) {
return x * x;
};
var num = 12;
console.log(square(num));
// → 144
console.log(num.square());
// → TypeError: undefined is not a function (line 9)
// does not work?
Thanks!
Syntax-wise, there's no "magic" happening here. You're describing two very different things.
In your first example, you're passing an Array to your own defined forEach method. In the second example, the forEach you're calling is actually a built-in method on Arrays (see MDN). If you changed your forEach to not invoke action, you'll see that yours breaks and the second one still works.
You can achieve this functionality yourself. You could add a function to Number to make your second example work (although this is not a recommended practice, as it could collide with future JavaScript features [and many other reasons] or break existing functionality):
Number.prototype.square = function() {
return this * this;
};
var num = 12;
var squared = num.square();
alert(squared);
If you remove the forEach function from your first example, it will still work, that is because the Array type has a builtin forEach function.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
Alternatively try to change your forEach function to each and you will see that it will no longer work.
What you are trying to do is add a new method to an existing type. This is not good practice in JavaScript.
The example with the array works because Array in JavaScript (ECMAScript5) defines a forEach function.
The example with the number doesn't work because Number doesn't define a square function. You can extend Number to include the square function like this:
Number.prototype.square = function(x) {
return this * this;
};

Why do we need apply method inside the constructor to invoke any method defined on the prototype object?

Why do we need apply method inside the constructor to invoke any method defined on the prototype object?
Code working:
function Test(){
this.x = [];
this.add.apply(this,arguments);
}
Test.prototype.add = function(){
for(var i=0; i < arguments.length; i++){
this.x.push(arguments[i]);
}
}
var t = new Test(11,12)
t.x //[11,12] this is fine
t.x.length //2 this is also fine
But when i directly call add inside constructor
Code not working:
function Test(){
this.x = [];
this.add(arguments);
}
Test.prototype.add = function(){
for(var i=0; i < arguments.length; i++){
this.x.push(arguments[i]);
}
}
var t = new Test(11,12);
t.x.length; //1 Not added all elements why?
This hasn't got anything to do with prototypes, it's got to do with how apply takes an array and then uses the values as arguments to the function to call. In this case, if you do
this.add(arguments);
It's doing exactly that. Calling add with the first argument being an array-like object, and you end up with x being an array where the first element is an array. new Test(1, 2, 3) will result in x = [ [1, 2, 3] ] (the inner array is actually an Arguments object, but it resembles an array). However if you do
this.add.apply(this, arguments);
It's essentially doing
this.add(arguments[0], arguments[1], arguments[2], ...);
And this way x ends up being an array of those elements, instead of an array within an array. I.e., with new Test(1, 2, 3) you'll get x = [1, 2, 3], without the extra array inbetween.

When to use return, and what happens to returned data?

What is the difference between:
function bla1(x){console.log(x)}
and
function bla(x){return console.log(x)}
In which cases should I use return?
also, when a value is returned from a function, what happens to it? is it stored somewhere?
What is the difference
The first function returns undefined (as it does not return anything explicitly), the second one returns whatever console.log returns.
In which cases should I use return?
When the function is generating some value and you want to pass it back to the caller. Take Math.pow for example. It takes two arguments, the base and the exponent and returns the base raised to the exponent.
When a value is returned from a function, what happens to it? is it stored somewhere?
If you want to store the return value, then you have to assign it to a variable
var value = someFunction();
This stores the return value of someFunction in value. If you call the function without assigning the return value, then the value is just silently dropped:
someFunction();
These are programming basics and are not only relevant to JavaScript. You should find a book which introduces these basics and in particular for JavaScript, I recommend to read the MDN JavaScript Guide. Maybe the Wikipedia article about Functions is helpful as well.
Return in a function is a way to pass back data from the function.
Example:
function test(){
var test = 1+1;
return test;
}
var feedback = test(); //feedback would now contain the value 2 if outputted.
We could also send a variable into the function and then return it back out.
Example 2:
function test(i){
i= i+1;
return i;
}
var feedback = test(1); //feedback would also output the value 2.
As you already mentioned, return gives you the possibility to call a function and save its returning value.
Here is a little example:
function bla(x) { return "blablabla"; }
If I call the method I will get a string with blablabla:
var blastring = bla(x);
alert(blastring);
This would result in an alert with blablabla.
With return you specify what the value of a function is. You can use this value to do further operations or to store it into a variable and so on.
Since console.log returns undefined, the examples in your question are equivalent, as a function not reaching a return statement will return undefined as well. But let me give you an example:
function sum(arr) {
var s = 0;
for (var index in arr) {
s += arr[index];
}
return s;
}
function prodsum(arr, scalar) {
return scalar * sum(arr);
}
console.log(prodsum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3));
The result will be 165. If we remove the returns, then both functions will return undefined:
function sum(arr) {
var s = 0;
for (var index in arr) {
s += arr[index];
}
s;
}
function prodsum(arr, scalar) {
scalar * sum(arr);
}
console.log(prodsum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3));
and the result will be undefined as well. Basically, if you want the function to have a conclusion or final value, then you have a return in it.
If you just add return; in the function. It stops the execution of the function.

JavaScript "arguments" -passing a function with other parameters

Function multiply below is passed a callback function "addOne". Function multiply returns [3,5,7].
Since the callback function addOne is one of the arguments of function multiply, and since the arguments of function multiply are all multiplied by 2, why doesnt the callback function itself (i.e. addOne) get multiplied by 2? In other words, instead of function multiply returning [3,5,7], I would have expected it to return [3,5,7, NAN] since the function is not a number?
Does JavaScript interpreter just somehow know not to multiply it by 2?
function addOne(a) {
return a + 1;
}
function multiply(a,b,c, callback) {
var i, ar = [];
for (i = 0; i < 3; i++) {
ar[i] = callback(arguments[i] * 2);
}
return ar;
}
myarr = multiply(1,2,3, addOne);
myarr;
Because your loop's condition is <3 (hehe) which means it won't subscript the callback (the last element).
You should consider making the callback the first argument always, and splitting the arguments like so...
var argumentsArray = Array.prototype.slice.call(arguments),
callback = argumentsArray.shift();
jsFiddle.
Then, callback has your callback which you can call with call(), apply() or plain (), and argumentsArray has the remainder of your arguments as a proper array.
This line for (i = 0; i < 3; i++) { is protecting you.
You stop before it hits the callback argument
Because you are running the the loop for the first 3 args only. i < 3 runs for i=0, i=1,i=2

Categories

Resources