Reputation: 2061
Coming from C, every function needs a name that is used to call that specific function. but in the JavaScript code below. There is no name at all. So how does function(func) { func(); }
calls the something.push(function()
?
Not to mention that something.push(function()
doesn't even have an argument, which from C perspective, this is wrong
var something = [],
object = {
a: true,
b: true,
c: true
}
for (let key in object){
something.push(function() {
console.log(key);
});
}
something.forEach(function(func) { func(); }):
Upvotes: 0
Views: 1370
Reputation: 135197
Coming from C, every function needs a name that is used to call that specific function. but in the JavaScript code below, there is no name at all.
Well in JavaScript, that's just not the case – functions can indeed be anonymous (nameless) functions. Sometimes you'll hear them called "lambdas" or redundantly called "lambda functions".
But more importantly, functions are first class data members in JavaScript, meaning:
They can be assigned to variables
let y = x => x + 5
y(3)
// 8
They can be passed as arguments to other functions
let xs = [1,2,3]
let y = x => 2 * x
xs.map(y)
// [2,4,6]
They can be returned from a function
let add = x => {
return y => x + y
}
let f = add(1)
f(2)
// 3
They can be included in data structures
let pikachu = {
number: 25,
shock: enemy => enemy.damage(30),
growl: enemy => enemy.sadden(40)
}
pikachu.growl(meowth)
// "It's very effective!"
So what's the take away here? Well, in JavaScript, you need to think of functions as being no different than any other value (eg) 1
, "foo"
, or [{user: 'bob'}, {user: 'alice'}]
– conceptually, they are just data and they're all first class
How to understand your code
Here's your code affectionally reformatted by me. Since you originally asked about ES6, I'm going to substitute your lambdas with ES6 arrow functions
let something = []
let object = { a: true, b: true, c: true }
for (let key in object) {
something.push(() => console.log(key))
}
something.forEach(func => func())
// a
// b
// c
Using high level descriptors, the nature of this code is
object
, add some value to something
something
, do something with that valueIn your case, some value is a function, but I want to show you what your code would look like if we used another kind of value
let something = []
let object = { a: true, b: true, c: true }
for (let key in object) {
// this time push a string, instead of an anonymous function
something.push("the key is: " + key)
}
something.forEach(str => console.log(str))
// the key is: a
// the key is: b
// the key is: c
So now we can see how the code works when a different value type (String) is used. Aside from using a string, the only other thing we did was change
// from
func => func()
// to
str => console.log(str)
The reasoning here is
in your original code, something
is an array of functions, so for each of those functions, func
, we call func()
in the modified code, something
is an array of strings, so for each of those strings, str
, we call console.log(str)
Dissection of Array.prototype.forEach
The last remaining bit to understand is the super-powered forEach
function. Remember point number 2 in the first class capabilities: first class data members can be passed as arguments to other functions.
forEach
expects its argument to be a function. So forEach
is a function that accepts another function. This kind of function is called a higher-order function.
Don't let these fancy names distract you from their underlying simplicity. Making our own higher-order functions like forEach
is actually really easy. Always just remember that functions are like any other value – below, we'll write our own version of forEach
so you can see how it works
let something = []
let object = { a: true, b: true, c: true }
// forEach accepts an array and some function, f
let forEach = (arr, f) => {
// iterate thru each value of arr
for (let value of arr) {
// call f on each value
f(value)
}
}
for (let key in object) {
something.push("the key is: " + key)
}
forEach(something, str => console.log(str))
// the key is: a
// the key is: b
// the key is: c
So now we see the string array working with our own forEach
function. Let's make sure it works with your original lambda array, too.
let something = []
let object = { a: true, b: true, c: true }
let forEach = (arr, f) => {
for (let value of arr) {
f(value)
}
}
for (let key in object) {
something.push(() => console.log(key))
}
forEach(something, func => { func() })
// a
// b
// c
Remarks
And that's it! Functions aren't special snowflakes in JavaScript. They're just like all the datums.
Coming from a C perspective, it might take awhile to think about functions in this way. But really, you should leave everything you understand about any language at the door when you're walking into the home of any new programming language. Each language has its own way of expressing itself yielding a unique set of strengths and weaknesses. By bringing in a "strength" of some language A to another language B, you might be substituting one of B's own strengths for one of it's weaknesses.
JavaScript is a multi-paradigm language making it rather suitable for imperative, object-oriented, and functional programming styles. Because of its jack-of-all-trades nature, it's probably not the best oop or fp language, but it's still rather impressive that it can express a particular solution to a problem in a wide variety of ways.
Upvotes: 2
Reputation: 506
What you see is the usage of an anonymous function and a closure. It's pretty Javascript in a nutshell.
There I describe the code block per block :
We create an array (like in C) :
var something = [],
We create an object (which is a collection of key=value)
object = {
a: true,
b: true,
c: true
}
We iterate through the elements of the object object
for (let key in object) {
There the show is on :
something.push(function() {
Each object has a push function that allow to put elements dynamically inside of it. What you see here is a anonymous function being used as an argument; it means that for each executions of this push function, a function that do :
console.log(key);
will be put inside the something array;
There after, the only thing that is done is a callback
with func() :
something.forEach(function(func) { func(); }):
This means forEach elements of something, you will treat it as a function resulting in calling the precedent function stored with push()
Upvotes: 0
Reputation: 239
So, what's happening here is in the loop, we are pushing a function
to something
on each iteration. Assuming something
is an array in this case.
Then, we call the collection's foreach() function. The first argument in this function is always the current item over which we are iterating. In this case, we're iterating over the list of functions we built in the loop. Since these are functions, we can call them, which is what's happening for each one.
Here's an example to help illustrate:
// declare some functions
function a(){console.log('hi');}
function b(){console.log('there');}
function c(){console.log('friend');}
const ar = [];
// push the functions to the array
ar.push(a);
ar.push(b);
ar.push(c);
ar.push(a);
ar.forEach(function(func) {func();});
// console output:
// hi
// there
// friend
// hi
EDIT: I highly recommend you read Mozilla's documentation of the function type. If you go there and thoroughly read through it I think you'll have a very solid grasp of this concept.
Upvotes: 0
Reputation: 185
JavaScript is more abstract language than C. In JavaScript interpretter internally saves types and pointers of every variable. When you declare a function, it is internally saved somewhere in memory and pointer of that function is passed to whatever you passed it (here to something.forEach
).
But, this is not pointer like in C, you cannot access its value (function address) nor you can change it. In JavaScript these pseudo-pointers are called "references". Simply, when you try to call something, interpretter engine will check its type. If its type type is a function, thread will do some preparations and then execute jump to memory location which function pointer is pointing to.
Upvotes: 0
Reputation: 9203
In JavaScript, functions are treated as objects. And in C you must be aware that not every object (value) needs to have a name, or doesn't need to be stored in a variable. Just like you can have sum (4, 5);
. 4 and 5 are values not stored in variables.
To answer your question about how they will be called if they have no name, the function is passed as an argument to the push function. There the formal parameter must have some name. So it can be called by that name. In this case push doesn't call it but stores it in an array to be called later. It can be called by indexing into the array.
Upvotes: 0
Reputation: 1488
The something
array is storing references to functions, but not invoking them. In the forEach
loop, each function within the array is being invoked.
To be fair, the naming is confusing. You could just as easily write:
something.forEach(function(storedFunction) { storedFunction(); }):
since it's a temporary variable
Upvotes: 0