Why function declarations are always moved to top during evaluation? - javascript

This isn't actually a specific issue I have but something I'd like to educate myself on:
As far as I understand, in JavaScript the following code
if (true) {
function greet(){ alert("Hello!"); }
} else {
function greet(){ alert("Hi!"); }
}
greet();
outputs Hi! because the code is actually evaluated as something like this:
greet = function(){ alert("Hello!"); }
greet = function(){ alert("Hi!"); }
if(true){
// possibly a no-op assignment, such as greet = greet?
}else{
// same as the other branch?
}
greet();
From a language design perspective, why does JavaScript behave in this way?

Named functions are created before the code starts, so that you don't have to put the declaration of a function before the code that uses it:
x(); // this is possible because the function already exists
function x() {}
It doesn't matter where the function declaration is, even if it's inside a flow control structure, it's still created before the code starts. So, there is no executable code at all inside the if statement in your example, not even a no-op assignment.
If you want to assign function declarations dynamically, you have use a variable:
var greet;
if (true) {
greet = function(){ alert("Hello!"); }
} else {
greet = function(){ alert("Hi!"); }
}
greet();
You don't have to use anonymous functions though, you could assign one of two named functions to the variable.

You may have found a quirk of the language and if you look a bit further you will also find different implementations.
ECMAScript processing has two phases - in the first, all declarations are processed to create named properties within the scope of their declaration. In the second, the code is executed. So declared functions and variables are available in their declared scope from the start of execution, regardless of where in the code they are declared.
And those that are created or assigned values by assignment will have values only when the assignment code is executed.
So:
// Call x
x();
// Declare x
function x(){}
works without error. This is colloquially called "hoisting", meaning declarations are "hoisted" to the top of the scope.
However, to paraphrase your example:
if (true) {
function x(){
alert('true x');
}
} else {
function x(){
alert('false x');
}
}
// In most browsers
x(); // false x
// But in Firefox
x(); // true x
For most browsers, the second declaration overrides the first. That is because ECMAScript does not have block scope, only function and global scope.
However, in this case extensions are allowed to the language. Firefox treats the above as named function expressions, hence is shows true x. More importantly, because other browsers treat the expressions as declarations, x can be called from above the code block but in Firefox it can't, you'll get an error.
The bottom line is if you want to conditionally assign a function to a named parameter or variable, do it explicitly like:
var x;
if (true) {
x = function (){
alert('true x');
};
} else {
x = function (){
alert('false x');
};
}
so that you get consistent behaviour. And you must also remember that x will not be a function until after the code has been executed, it will not be "hoisted" because it isn't a declaration.

First, it does not always behave in this way. In the case of Function Expressions function object is only available in the internal scope.
Second, when you define a function it is very likely that you want to use it in the current scope. So it is very natural that it will become available in the current scope. Consider that the enclosing scope (depending on the function definition location) is the global object or the enclosing function body not the if block.
Now, I can reduce your question to the following question:
Why the function name is used as the variable name?
A : Otherwise developers have to always assign function to a variable (which I prefer).

Related

Running a function with an undefined reference

As I understand it, Javascript doesn’t compile, it only runs. So there should be no compile-time errors, only runtime errors. So why doesn’t this code work?
function show() { console.log(x); }
(function () {
var x = 42;
show();
})()
My question isn’t on how to make this code better; I realize it is bad code and I already know how to fix it (see below).
My question is, why am I getting an Uncaught ReferenceError? If Javascript only throws errors at runtime, it should know that x == 42 at the time it calls show(), which is inside the anonymous function, correct?
working code:
(function () {
var x = 42;
function show() { console.log(x); }
show();
})()
working code, best option:
function show(y) { console.log(y); }
(function () {
var x = 42;
show(x);
})()
Note:Below description is as of ES5, NOT ES6 since in ES6 scoping rules have been changed(due to introduction of let).
Javascript does compile. It's just that like other languages such as c++/c# there is NO intermediate thing like exe/IL code that need to be clicked to start execution. In JS execution starts after compile phase.
So, when the compilation happens the compiler looks for function declaration and var declaration for variables. Therefore for this IIFE,
(function () {
var x = 42;
show();
})();
It does find one var declaration and the variable x is registered in the scope of the IIFE. This is called variable hoisting and x is available at the function level, in this case IIFE.
Later at execution time, the IIFE looks like this(conceptually):
(function () {
//registration of x in this function's scope has already happened at
//compile time. Notice absence of `var`
x = 42;
show();
})();
Now, at this time the engine talks to scope and asks for lvalue reference of x. Since x was registered in IIFE, engine gets one and then 42 is assigned to it.
Now for this part:
function show() { console.log(x); }
When show is called, engine first asks scope of show function for x, since it doesn't have any variable registered with the name x, global scope is asked( rvalue reference) for x, since it was never registered with global scope either during compilation phase, it cannot be found in any of the scope and we get reference error.
it should know that x == 42 at the time it calls show(), which is inside the anonymous function, correct?
Because of scoping rules, variables in outer scopes are visible in inner scope but not vice-versa. x inside IIFE is visible at the IIFE level and not outside in outer scope.
Variable hoisting in JS is fun.
It's saying giving you the Reference Error because you've defined x inside of a closure (a function defined inside another function), meaning it's not available in the global scope and the show() method doesn't know it exists. If you defined it first, globally, it would of course work.
That said, scoping has been significantly improved in ES6+ with the use of let and const, so unless you're stuck with vanilla JS you'll probably find those to give a much more consistent and predictable coding experience.
This is a useful read on the subject: How do JavaScript closures work?
show function gets scope where it's declared, not invoked.

Javascript named function definition executed when it shouldn't

I have no idea what is happening here. The code is:
if( true ) {
console.log('In first function definition');
function test(){
console.log('Hello world');
}
} else {
console.log('In the second function definition');
function test(){
console.log('Goodbye world');
}
}
test();
I would expect this to log in the console:
'In the first function definition'
'Hello world'
But instead it logs:
'In the first function definition'
'Goodbye world'
How is the second named function being created when the code doesn't enter that branch?
Remember, everything in JavaScript is function-scoped; there is no block scope.
To that effect, you have two function declarations defined in the same scope (whatever the scope that if-else sits in), with the same name, in different blocks. This produces inconsistent results between browsers.
You need to either give those functions different names, or use function expressions, with something like this:
var f;
if(true) {
console.log('In first function definition');
f = function(){
console.log('Hello world');
};
} else {
console.log('In the second function definition');
f = function(){
console.log('Goodbye world');
};
}
f();
To your excellent question of
But how is the function defined, if we don't enter that branch of the code? If that block is not executed, why is scope a consideration?
The simple answer is that function declarations are "hoisted", and immediately available anywhere in the scope, even before the declaration itself.
ie:
function foo(){
f(); //perfectly valid
function f(){
}
}
The longer answer is that, prior to entering the scope, all variable declarations, and function declarations are stripped out, and placed onto the "activation object." The activation object is then placed at the head of the "scope chain." When you execute the function, any references to these variables and functions are simply resolved from there.
Function definitions in javascript are independent of control structures, meaning that when you redefine the function the second time even though it's in a branch of a control structure that will never bit hit it still redefines the function. What are you trying to do anyway?
Some JavaScript engines treat the definition as always occurring regardless of the condition, and the second definition overwrites the first.
Note: Some JavaScript engines, not including SpiderMonkey, incorrectly treat any function expression with a name as a function definition.
To add to Adam's answer, you can get a way around it if you assign your function.
if( true ) {
console.log('In first function definition');
test = function() { console.log('Hello world'); }
} else {
console.log('In the second function definition');
test = function(){ console.log('Goodbye world'); }
}
test();
This will print
In the first function definition
Hello world
According to ECMA-262, function declarations are processed before any code is executed. So declaring functions anywhere effectively moves ("hoists") them to the top of the execution context.
However, not everything that looks like a declaration is a declaration. In the following:
if (false) {
function fred() {return true;}
}
Some browsers see a function declaration so:
alert(fred());
shows "true". Other browsers use an extension to ECMA-262 that allows it to be treated as a named function expression function statement (I'll try to find a reference to an excellent posting by Richard Cornford on comp.lang.javascript about that see below), so that:
alert(fred());
throws as an error that fred is undefined. Try it in Firefox and IE.
So the bottom line is if you want to conditionally create a function, use an unambiguous function expression. They're often used in conjunction with feature testing, e.g. to create a function to get the textContent or innerText of an element:
var getText = (function() {
var el = document.createElement('div');
if (typeof div.textContent == 'string') {
div = null;
return function(el) {return el.textContent;};
} else if (typeof div.innerText == 'string') {
div = null;
return function(el) {return el.innerText;};
}
}());
Edit
Here's Richard Cornford's post in FunctionExpression's and memory consumptions. The important part:
In fact it is a function statement; a syntax extension that is
capable of allowing the conditional creation of a function because
being a Statement it can be evaluative inside a block.
But the whole post is worth reading.
Also note that function statements are warned against in ES5 strict mode and may be introduced in some future version of ECMAScript.

What's the difference between naming a function (as opposed to leaving it anon) and creating a reference to it?

In a comment on another thread I started, someone said this:
#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. – valentinas 6 hours ago
What purpose does a function name serve if you can't call it with bar()?
For the function to call itself.
var x = function y(val){
if (val){
console.log(val);
y(val-1);
}
};
x(5);
> 3
> 2
> 1
y(3);
> ReferenceError: y is not defined
You're referring to a named function expression. The spec requires that the name of such functions only be available within the scope of the new function. Spec quote:
The Identifier in a FunctionExpression can be referenced from inside
the FunctionExpression's FunctionBody to allow the function to call
itself recursively. However, unlike in a FunctionDeclaration, the
Identifier in a FunctionExpression cannot be referenced from and does
not affect the scope enclosing the FunctionExpression.
On the other hand, the result of that expression is a reference to the new function, which can be saved and referenced anywhere.
Lots of details here:
http://kangax.github.com/nfe/#named-expr
I'd read the whole thing.
As for benefits, another is that it makes them easier to identify in a debugger.
There are two ways to create a function in JavaScript, a "function declaration" and a "function expression." I believe it was Doug Crockford who explained it best when he pointed out that unless "function" is the very first set of characters on a given line, you're performing a function expression (not a declaration).
Function declarations are finicky creatures. You'll recognize them when you see them. They look like this:
function foo() { /* ... */ }
They're always given a name (it's requited) and the name is locally scoped to the lexical context under which the function is declared. So if you perform a function declaration in the global context, then the function can be referenced via it's name globally. If you do it within a function, the function's name can be referenced only within that function and any functions declared within that function.
I think the most important aspect of this method of declaring a function (one that is rarely commented on) is that the function initialization gets hoisted to the top of the current lexical context. Therefore, you should never, ever use a function declaration within a conditional, such as this:
//DON'T DO THIS!
if (x) {
function foo() { return 1; }
} else {
function foo() { return 2; }
}
foo(); //will always be 2, regardless of the value of x.
A function expression is slightly different. Often, they're directly assigned to a variable, like so:
var foo = function() { /* ... */ };
This is nearly identical to the function declaration above except that the initialization is not hoisted. So you can do the following:
var foo;
if (x) {
foo = function() { return 1; };
} else {
foo = function() { return 2; };
}
foo(); //will be 1 or 2, depending on the truthy-ness of x.
So, back to the original question. Function expressions can also have a name, though it's not required and it's not scoped to the context in which the function is declared (as with function declarations). Instead, it gets scoped to the function's own lexical context. This is very useful in some cases. My personal favorite is this pattern:
(function foo() {
//Do something.
setTimeout(foo, 1000);
}());
foo; //undefined
Because of the parenthesis before the word "function", this is a function expression and the name is scoped internally only. But that's okay, because we only need to call it internally (via setTimeout()). The result is that the function will execute once immediately, then will re-execute every second or so after it's finishes execution. This is safer than using setInterval() because it will wait until it's done executing before rescheduling itself, preventing overlaps that could cause missed executions and/or "domination" of the JavaScript thread.
Essentially, the use of a named function expression is limited, but when you need it, it's very powerful.

JavaScript scope in a try block

Say I'm trying to execute this JavaScript snippet. Assume the undeclared vars and methods are declared elsewhere, above, and that something and somethingElse evaluate to boolean-true.
try {
if(something) {
var magicVar = -1;
}
if(somethingElse) {
magicFunction(magicVar);
}
} catch(e) {
doSomethingWithError(e);
}
My question is: what is the scope of magicVar and is it okay to pass it into magicFunction as I've done?
Lots of other good answers about how Javascript handles this with var, but I thought I'd address the let situation...
If a variable is defined with let inside the try block, it will NOT be in scope inside the catch (or finally) block(s). It would need to be defined in the enclosing block.
For example, in the following code block, the console output will be "Outside":
let xyz = "Outside";
try {
let xyz = "Inside";
throw new Error("Blah");
} catch (err) {
console.log(xyz);
}
Javascript has function scope. That means that magicvar will exist from the beginning of the function it's declared in all the way to the end of that function, even if that statement doesn't ever execute. This is called variable hoisting. The same thing happens with functions declarations, which in turn is called function hoisting.
If the variable is declared in global scope, it will be visible to everything. This is part of the reason why global variables are considered evil in Javascript.
Your example will pass undefined into magicFunction if something is false, because magicVar hasn't been assigned to anything.
While this is technically valid Javascript, it's generally considered bad style and will not pass style checkers like jsLint. Extremely unintuitive Javascript like this will execute without any error
alert(a); //alerts "undefined"
var a;
POP QUIZ: What does the following code do?
(function() {
x = 2;
var x;
alert(x);
})();
alert(x);
In javascript only functions create a new context -closure.
Every definition of a variable is really a declaration of the variable at the top of its scope and an assignment at the place where the definition is.
var
function-scoped
hoist to the top of its function
redeclarations of the same name in the same scope are no-ops
You may want to read MDN scope cheat sheet
Due to hoisting You can even do things like this:
function bar() {
var x = "outer";
function foo() {
alert(x); // {undefined} Doesn't refer to the outerscope x
// Due the the var hoising next:
x = 'inner';
var x;
alert(x); // inner
}
foo();
}
bar();​
bar();​
Demo
So the foo function is converted to something like this:
function foo() {
var x;
alert(x); // {undefined} Doesn't refer to the outerscope x
// Due the the var hoising next:
x = 'inner';
alert(x); // inner
}​
My question is: what is the scope of magicVar and is it okay to pass it into magicFunction as I've done?
Define okay..., Yes the code is valid, but it's less readable then if the variables declarations were on the top, that's all.
Due to javascript "hoisting" (MDN description), your variable declaration code gets translated as:
function yourFunction() {
var magicVar;
try {
if(something) {
magicVar = -1;
}
if(somethingElse) {
magicFunction(magicVar);
}
} catch(e) {
doSomethingWithError(e);
}
} //end of your function
"Hoisting" moves all variables declarations to the top of the function. So magicVar is available everywhere in the function, but it's undefined until you give it a value.
Your variable has function scope.
With var, variables exist from the beginning of the function to the end of it, no matter where they are declared, or even if the statement is actually ever reached. They will, however, be undefined until they are assigned another value.
So in your case, if something is false but somethingelse is true, you will call magicFunction with its first argument being undefined.
The let keyword, created in Javascript 1.9 and available (as of today, May 3rd 2012, and as far as I know) only in Firefox, declares variables with the scoped semantics you're probably used to.
I agree with variable hoisting and function hoisting, I would like to emphasis two import points.
Identifier Defined in Catch parameter is i.e. err/e(error) , is scoped to Catch defined block.
Function first hoisting.
example :
b(); // output : 3
var b = 2;
function b(){
return 3;
}

No need to define a variable twice

If a variable could be defined in a function, even if no value is assigned, it becomes a local variable
so, is testB() better programming?
var test = 'SNAP!'
function testA(boolean) {
if (boolean) var test = 'OK';
else var test = null;
alert(test);
}
function testB(boolean) {
if (boolean) var test = 'OK';
alert(test);
}
testA(true); // 'OK'
testB(true); // 'OK'
testA(false); // null
testB(false); // undefined, no error
In my specific case test's global value ('SNAP!') is neither expected nor required.
You can't declare variables conditionally.
Why?
The variable instantiation process occurs before the actual code execution, at the time the function is executed, those variables will be already bound to the local scope, for example:
function foo () {
if (false) {
var test = 'foo'; // never executed
}
return test;
}
foo(); // undefined
When the function is about to be executed, identifiers of formal parameters, identifiers from variable declarations, and identifiers from function declarations within the function's body are bound to the local variable environment.
Variables are initialized with undefined.
Also, identifiers in the local scope shadow the others with the same name, higher in the scope chain, for example:
var test = 'global';
function bar () {
alert(test); // undefined, not "global", the local variable already declared
var test = 'xxx';
}
bar();
If the test variable were not declared anywhere, a ReferenceError will be thrown:
function foo () {
return test;
}
try {
foo(); // ReferenceError!!
} catch (e) {
alert(e);
}
That's one of the reasons about why for example, JSLint recommends only one var statement at the top of functions, because for example, the first snippet, will actually resemble this when executed:
function foo () {
var test; // var statement was "hoisted"
if (false) {
test = 'foo'; // never executed
}
return test;
}
foo(); // undefined
Another reason is because blocks don't introduce a new lexical scope, only functions do it, so having a var statement within a look might make you think that the life of the variable is constrained to that block only, but that's not the case.
Nested function declarations will have a similar behavior of hoisting, they will be declared before the code execution, but they are initialized in that moment also:
function foo () {
return typeof bar;
// unreachable code:
function bar() {
//..
}
}
foo(); // "function"
If the variable does not need to be manipulated by any other functions, keep the variable inside a function with var foo;.
Otherwise, if it does need to be accessed and read in multiple scopes, keep it outside. But remember that when you keep it outside, it becomes global. That is, unless you wrap everything in a self executing function, which is the best way:
(function() {
var president='bush';
function blah() {
president='reagan';
}
function meh() {
president= 'carter';
}
document.getElementById('reagan').onclick=blah;
document.getElementById('carter').onclick=meh;
})();
alert( president ) // undefined
The above is perfect for a variable accessed by functions defined inside of that scope. Since there are 2 elements i click to set the president, it makes sense to define it outside both functions because they set the same variable.
So, If you are not dealing with multiple functions changing the exact same variable, keep them local to the function.
Is testB better programming? No, because it gives an unexpected result of "undefined" (at least, I was surprised by that) and it is hard to read.
Generally, variables should be limited to the scope that requires them, so if the "test" variable is not needed outside the function it should be declared local. To avoid confusion, declare your variable before using it:
function testC(boolean) {
var test;
if (boolean) {
test = "OK";
}
else {
test = null;
}
alert(test);
}
Unless you genuinely want to change the global scope version of "test", in which case don't use the var keyword inside the function.
If you ever find yourself using the same name for a local variable and a global variable you might consider renaming one of them.

Categories