If functions in JS are first-class, what allows them to be called before they are defined?
Asked Answered
S

4

18

Don't first class functions mean that they behave as variables? Clearly they don't behave exactly like variables though, since this:

console.log(foo);
var foo = 'bar';

...doesn't work, whereas this:

console.log(foo());
function foo() {
 return('bar');
}

...does.

That said, this:

console.log(foo());
var foo = function() { return 'bar'; };

doesn't work, which is more consistent.

What gives?

Stun answered 5/9, 2012 at 3:13 Comment(3)
"Doesn't first class functions mean that they behave as variables?" - No, that's not what "first class" means. You need to understand the difference between a "variable" and a "value", and that (in JS) the "value" may be a primitive (e.g., a number) or a reference to an object - and functions are a type of object. It may help to hover your mouse over the "first-class-functions" tag and read the description that pops up...Matrona
That's essentially, AFAIK, the definition of a variable...Stun
@wwaawaw: Not really. A variable is simply named storage. Values are the things that can be stored in variables.Baculiform
R
10

Because you don't compare the same thing. In your example - you compare function declaration function foo()... with variable declaration and assignment in var foo = 'bar';

A more correct comparison would be of:

console.log(foo);
var foo = 'bar';

with

console.log(foo());
var foo = function() {
 return 'bar';
}

The functional declaration is interpreted differently due to the way hoisting works. Hoisting moves all declarations to the top of the closest scope, while leaving assignments in their place.

Function declaration is special in that sense, since it's both declaration and expression/assignment in one statement and thus hoisted together.

As an example: you can look at expressions like:

console.log(foo);
var foo = 'bar';

as this:

var foo;
console.log(foo); //prints undefined
foo = 'bar';

and

console.log(foo());
var foo = function() {
 return 'bar';
}

as this:

var foo;
console.log(foo());
foo = function() {
 return 'bar';
}
Regenerative answered 5/9, 2012 at 3:24 Comment(3)
Seems like coffeescript makes this really explicit and takes it out from behind the scenes.Stun
So then console.log(foo()); function foo(){return 'bar';} would be the equivalent of var foo = function(){return 'bar';}; console.log(foo());, and both would print bar, correct?Stun
Obviously I meant semantically equivalent. Accepted, BtW. Thank you for actually explaining what's really going on instead of just how to use it. Your knowledge of the language doesn't improve at all without answers being linked to the broader linguistic concepts as yours is. Thanks again.Stun
A
18

What you're experiencing is called hoisting. When using a function declaration like:

function foo() {}

foo will be moved to the top of the closest scope (function).

On the other hand when you use a function expression or function assignment like:

var foo = function() {}

the variable foo will be moved to the top but the assignment will occur when is needed.

Learn more

Akerley answered 5/9, 2012 at 3:18 Comment(5)
Is scope actually fully synonymous with function?Stun
In JavaScript yes, scopes are functions, there is no block scope.Akerley
@Akerley which is so sad in cases like for(var i...)Bartolomeo
Function declarations in blocks are actually not officially supported in current JavaScript. ES6 on the other hand will have block scope, and block-scoped function declarations.Jazzman
@adlwalrus Nope, because there is a global scope in addition to function local scope.Osithe
R
10

Because you don't compare the same thing. In your example - you compare function declaration function foo()... with variable declaration and assignment in var foo = 'bar';

A more correct comparison would be of:

console.log(foo);
var foo = 'bar';

with

console.log(foo());
var foo = function() {
 return 'bar';
}

The functional declaration is interpreted differently due to the way hoisting works. Hoisting moves all declarations to the top of the closest scope, while leaving assignments in their place.

Function declaration is special in that sense, since it's both declaration and expression/assignment in one statement and thus hoisted together.

As an example: you can look at expressions like:

console.log(foo);
var foo = 'bar';

as this:

var foo;
console.log(foo); //prints undefined
foo = 'bar';

and

console.log(foo());
var foo = function() {
 return 'bar';
}

as this:

var foo;
console.log(foo());
foo = function() {
 return 'bar';
}
Regenerative answered 5/9, 2012 at 3:24 Comment(3)
Seems like coffeescript makes this really explicit and takes it out from behind the scenes.Stun
So then console.log(foo()); function foo(){return 'bar';} would be the equivalent of var foo = function(){return 'bar';}; console.log(foo());, and both would print bar, correct?Stun
Obviously I meant semantically equivalent. Accepted, BtW. Thank you for actually explaining what's really going on instead of just how to use it. Your knowledge of the language doesn't improve at all without answers being linked to the broader linguistic concepts as yours is. Thanks again.Stun
B
6

Function declarations are automatically bumped to top of the scope in JS

console.log(foo());
function foo() {
 return('bar');
}

is actually interpreted as

function foo() {
 return('bar');
}
console.log(foo());

the second bit of code is working that way, because foo is variable, not a function (it just happens to have an anonymous function as a value). Variables are also bumped to top, so

console.log(foo());
var foo = function() { return 'bar'; };

becomes

var foo; //empty variable
console.log(foo()); //undefined
foo = function() { return 'bar'; }; //creates a function without name and assigns it to foo
Bartolomeo answered 5/9, 2012 at 3:18 Comment(5)
Is that to say that ``` function foo(){ return 'bar'; } ``` doesn't also create a variable named foo with the function function(){return 'bar';} as its value?Stun
@adlwalrus it creates the variable, but the value is function foo(){return 'bar';} (named function, not anonymous).Bartolomeo
So functions have "names" in addition to those of the variables that hold them as their values?Stun
@adlwalrus yes. try this: var foo = function bar(){}; console.log(foo); But be aware that bar is only function name (what does it mean I'm not sure exactly myself) and not a reference to it, so you can't call it by doing bar(). And assigning (even named function) is not the same as declaring a function. Hoisting (bumping to top of the scope) only works for declarations, assignment will stay in place.Bartolomeo
Thanks man, great answer, and very interesting. Grounds for a new question, I'd say.Stun
K
4

Function declaration and variable declaration will always be moved to the top of their scope.

console.log(foo());
function foo() {
   return 'bar';
}

is interpreted as:

function foo() {
   return 'bar';
}
console.log(foo());

console.log(foo());
var foo = function () {
   return 'bar';
};

is interpreted as:

var foo;
console.log(foo());
foo = function () {
   return 'bar';
};
Khiva answered 5/9, 2012 at 3:24 Comment(2)
Oh, I see. Actually the most complete answer here. Accepted.Stun
Actually I take the acceptance back because ZenMaster's seems more thorough. Still great answer though.Stun

© 2022 - 2024 — McMap. All rights reserved.