Suhaib
Suhaib

Reputation: 2061

Trying to understand (function(func) { func(); } in JavaScript

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

Answers (6)

Mulan
Mulan

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:

  1. They can be assigned to variables

    let y = x => x + 5
    y(3)
    // 8
    
  2. 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]
    
  3. They can be returned from a function

    let add = x => {
      return y => x + y
    }
    let f = add(1)
    f(2)
    // 3
    
  4. 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

  1. iterate thru each key of object, add some value to something
  2. iterate thru each value of something, do something with that value

In 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

Plotisateur
Plotisateur

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

Justin K
Justin K

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

user7771338
user7771338

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

Ajay Brahmakshatriya
Ajay Brahmakshatriya

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

Bobby Matson
Bobby Matson

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

Related Questions