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;
}
Related
No matter whether I define the function after the variable
var a = 1;
function a() {};
typeof a // number
Or if I define the function before the variable
function a() {};
var a = 1;
typeof a // number
the final typeof result is always number
I found some explanation about execution context in http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/
Before executing the function code, create the execution context.
......
Scan the context for variable declarations:
If the variable name already exists in the variable object, do nothing and continue scanning.
but this does not seem to work.
So how can I explain it?
It's to do with JavaScript's variable hoisting. Try this instead:
var a = 1;
var a = function() {};
typeof a // function
You're implicitly declaring the variable multiple times by using the function statement 'function a() {};', which as noted by others hoists the variable and behaves unexpectedly due to the order that the browser registers the declarations.
Behind the scenes, this statement instantiates a function object and assigns the result to the variable passed as the function name (reference) but this is done before the explicit var declarations are performed, and so that overrides the implicit declaration. If you just do the following, it will work more intuitively:
var a = 1;
a = function(){};
console.log(typeof a); // function
This is a better option than the multiple var declaration in the other answer from a logical standpoint because (even though you can), it's not a good practice to declare the variable multiple times anyway.
To specifically answer the 'why' for this question: it's so that you can use these kinds of statements to define functions and use them in your explicit declarations, as in
var a = someFunction();
function someFunction(){ return 'someVal'; }
If the function statements weren't parsed and hoisted first, this wouldn't be possible.
As already mentioned, it has to do with the way JavaScript hoisting works. The main issue to notice is that JavaScript will hoist the complete function definition (along with the function body) up to the top, but keeps the variable initialization where it is (only the declaration is hoisted).
So if you write this:
var a = 1;
function a () {
}
it will be translated to:
var a;
function a() {
}
a = 1;
and if you write this:
function a () {
}
var a = 1;
it will be translated to:
function a () {
}
var a;
a = 1;
So no matter what you do, a = 1; will remain at the very bottom.
Please note that the above "translations" should be seen theoretically. JavaScript probably has a way to omit the var a; statement if there is already a function declaration with the same name. And there also might be a defined order (functions get hoisted before variables or the other way around). But all of this doesn't affect the outcome of the variable initialization being the only part that is NOT hoisted at all.
I have been taught to define variables at the top, regardless of their position in your code, as this is how JavaScript will interpret things. So, my understanding is that:
var foo = "Bob";
if (2 + 2 === 4) {
var car = "Blah";
}
Will be interpreted like:
var foo = "Bob",
car;
if (2 + 2 === 4) {
car = "Blah";
}
Is my understanding correct? I've always tried to position my variable definitions at the top of the current scope, but sometimes those variables are only needed inside an if statement, so defining them outside seems a bit odd - is this still best practice?
Yes. var statements are hoisted (which is why best practise is to use them at the top of the function — it avoids confusion from people assuming block scope instead of function scope)
Yes. Variable and function declarations are hoisted to the top of the scope in which they are defined. Since JavaScript only has function scope (and not block scope), the top of the scope in your example is outside the if statement.
Note that since function expressions are effectively just variable declarations, the function itself is not hoisted (as assignment occurs where you intend it to). That means it's only available after the assignment...
sayHello(); //Uh-oh... TypeError, undefined is not a function!
var sayHello = function() {
console.log("Hi!");
};
... as opposed to a function declaration, which can be used before it is defined in the source:
sayHello(); //"Hi!"
function sayHello() {
console.log("Hi!");
}
Yes, it is always good to define them on top of the scope (and javascript is function scoped), you could read more about this here.
Today, I got completely surprised when I saw that a global variable has undefined value in a certain case.
Example:
var value = 10;
function test() {
//A
console.log(value);
var value = 20;
//B
console.log(value);
}
test();
Gives output as
undefined
20
Here, why is the JavaScript engine considering global value as undefined? I know that JavaScript is an interpreted language. How is it able to consider variables in the function?
Is that a pitfall from the JavaScript engine?
This phenomenon is known as: JavaScript Variable Hoisting.
At no point are you accessing the global variable in your function; you're only ever accessing the local value variable.
Your code is equivalent to the following:
var value = 10;
function test() {
var value;
console.log(value);
value = 20;
console.log(value);
}
test();
Still surprised you're getting undefined?
Explanation:
This is something that every JavaScript programmer bumps into sooner or later. Simply put, whatever variables you declare are always hoisted to the top of your local closure. So, even though you declared your variable after the first console.log call, it's still considered as if you had declared it before that.
However, only the declaration part is being hoisted; the assignment, on the other hand, is not.
So, when you first called console.log(value), you were referencing your locally declared variable, which has got nothing assigned to it yet; hence undefined.
Here's another example:
var test = 'start';
function end() {
test = 'end';
var test = 'local';
}
end();
alert(test);
What do you think this will alert? No, don't just read on, think about it. What's the value of test?
If you said anything other than start, you were wrong. The above code is equivalent to this:
var test = 'start';
function end() {
var test;
test = 'end';
test = 'local';
}
end();
alert(test);
so that the global variable is never affected.
As you can see, no matter where you put your variable declaration, it is always hoisted to the top of your local closure.
Side note:
This also applies to functions.
Consider this piece of code:
test("Won't work!");
test = function(text) { alert(text); }
which will give you a reference error:
Uncaught ReferenceError: test is not defined
This throws off a lot of developers, since this piece of code works fine:
test("Works!");
function test(text) { alert(text); }
The reason for this, as stated, is because the assignment part is not hoisted. So in the first example, when test("Won't work!") was run, the test variable has already been declared, but has yet to have the function assigned to it.
In the second example, we're not using variable assignment. Rather, we're using proper function declaration syntax, which does get the function completely hoisted.
Ben Cherry has written an excellent article on this, appropriately titled JavaScript Scoping and Hoisting.
Read it. It'll give you the whole picture in full detail.
I was somewhat disappointed that the problem here is explained, but no one proposed a solution. If you want to access a global variable in function scope without the function making an undefined local var first, reference the var as window.varName
Variables in JavaScript always have function-wide scope. Even if they were defined in the middle of the function, they are visible before. Similar phenomena may be observed with function hoisting.
That being said, the first console.log(value) sees the value variable (the inner one which shadows the outer value), but it has not yet been initialized. You can think of it as if all variable declarations were implicitly moved to the beginning of the function (not inner-most code block), while the definitions are left on the same place.
See also
Javascript function scoping and hoisting
Javascript variable declarations at the head of a function
There is a global variable value, but when control enters the test function, another value variable is declared, which shadows the global one. Since variable declarations (but not assignments) in JavaScript are hoisted to the top of scope in which they are declared:
//value == undefined (global)
var value = 10;
//value == 10 (global)
function test() {
//value == undefined (local)
var value = 20;
//value == 20 (local)
}
//value == 10 (global)
Note that the same is true of function declarations, which means you can call a function before it appears to be defined in your code:
test(); //Call the function before it appears in the source
function test() {
//Do stuff
}
It's also worth noting that when you combine the two into a function expression, the variable will be undefined until the assignment takes place, so you can't call the function until that happens:
var test = function() {
//Do stuff
};
test(); //Have to call the function after the assignment
The simplest way to keep access to outer variables (not just of global scope) is, of course, to try to not re-declare them under the same name in functions; just do not use var there. The use of proper descriptive naming rules is advised. With those, it will be hard to end up with variables named like value (this aspect is not necessarily related to the example in the question as this variable name might have been given for simplicity).
If the function might be reused elsewhere and hence there is no guarantee that the outer variable actually defined in that new context, Eval function can be used. It is slow in this operation so it is not recommended for performance-demanding functions:
if (typeof variable === "undefined")
{
eval("var variable = 'Some value';");
}
If the outer scope variable you want access to is defined in a named function, then it might be attached to the function itself in the first place and then accessed from anywhere in the code -- be it from deeply nested functions or event handlers outside of everything else. Notice that accessing properties is way slower and would require you to change the way you program, so it is not recommended unless it is really necessary: Variables as properties of functions (JSFiddle):
// (the wrapper-binder is only necessary for using variables-properties
// via "this"instead of the function's name)
var functionAsImplicitObjectBody = function()
{
function someNestedFunction()
{
var redefinableVariable = "redefinableVariable's value from someNestedFunction";
console.log('--> functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('--> redefinableVariable: ', redefinableVariable);
}
var redefinableVariable = "redefinableVariable's value from someFunctionBody";
console.log('this.variableAsProperty: ', this.variableAsProperty);
console.log('functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('redefinableVariable: ', redefinableVariable);
someNestedFunction();
},
functionAsImplicitObject = functionAsImplicitObjectBody.bind(functionAsImplicitObjectBody);
functionAsImplicitObjectBody.variableAsProperty = "variableAsProperty's value, set at time stamp: " + (new Date()).getTime();
functionAsImplicitObject();
// (spread-like operator "..." provides passing of any number of arguments to
// the target internal "func" function in as many steps as necessary)
var functionAsExplicitObject = function(...arguments)
{
var functionAsExplicitObjectBody = {
variableAsProperty: "variableAsProperty's value",
func: function(argument1, argument2)
{
function someNestedFunction()
{
console.log('--> functionAsExplicitObjectBody.variableAsProperty: ',
functionAsExplicitObjectBody.variableAsProperty);
}
console.log("argument1: ", argument1);
console.log("argument2: ", argument2);
console.log("this.variableAsProperty: ", this.variableAsProperty);
someNestedFunction();
}
};
return functionAsExplicitObjectBody.func(...arguments);
};
functionAsExplicitObject("argument1's value", "argument2's value");
I was running into the same problem even with global variables. My problem, I discovered, was global variable do Not persist between html files.
<script>
window.myVar = 'foo';
window.myVarTwo = 'bar';
</script>
<object type="text/html" data="/myDataSource.html"></object>
I tried to reference myVar and myVarTwo in the loaded HTML file, but received the undefined error.
Long story/day short, I discovered I could reference the variables using:
<!DOCTYPE html>
<html lang="en">
<!! other stuff here !!>
<script>
var myHTMLVar = this.parent.myVar
/* other stuff here */
</script>
</html>
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).
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.