I'm learning Javascript at the minute and have a question about hoisting/scoping - perhaps I'm missing something.
If I define a global variable, I cant reference that variable's value inside a function because its out of scope?
var x = "hello";
function temp(){
console.log(x);
}
turns out to be
var x = "hello";
function temp(){
var x;
console.log(x);
}
which both outputs undefined. What are the points of global variables or how do you use them inside a function? - As I said what am i missing here! :)
Also hoisting works on functions? But NOT on anonymous functions? Correct?
Any help is appreciated!
Thanks!
Hoisting only applies to local variables (those declared with var in the current scope). Since there's no var in temp(), there's no hoisting.
Here x will be hoisted:
var x = "hello";
function temp(){
console.log(x); // undefined
var x = 55
}
temp()
because this is interpreted as:
var x = "hello";
function temp(){
var x /* = undefined */
console.log(x);
x = 55
}
temp()
You can access any variable that is defined in your scope or higher. If that same variable name is redeclared within your scope, then that becomes a new variable definition that overrides the other one.
So, with this example:
var x = "hello";
function temp(){
console.log(x); // outputs "hello" when temp() is called
}
x is defined at a higher scope than the function so it can be used inside the function.
In this example:
var x = "hello";
function temp(){
var x;
console.log(x); // outputs "undefined" when temp() is called
// because local var x has not been assigned a value
}
You've defined a new variable x which is a local variable inside the function and it's value will be undefined until you assign it something.
The term "hoisting" means that a variable defined anywhere within a scope (e.g. within a function) behaves as if it was defined at the very beginning of the function no matter where the declaration actually appears.
So, because of hoisting, this example:
var x = 10;
function temp() {
x = 3;
y = 4;
var x;
console.log(x);
}
behaves like this and all references to x within the function are to the local version of x:
var x = 10;
function temp() {
var x;
x = 3;
y = 4;
console.log(x);
}
Related
In the following code snippet how can we refer the global variable x values as product in the eval function
<script type="text/javascript">
var x = 'product';
window.onload = function() {
function somefunction() {
var x = 'boat';
alert(eval('x'));
}
somefunction();
};
You can use window object to make your variable global and use window.x to access it.
var x = 'product';
function somefunction() {
var x = 'boat';
console.log("logging global variable window.x: "+eval('window.x')); // resolve conflicts by using window.x
}
somefunction();
console.log("logging global variable x: "+ x); // access global variable..
So the changes you need to apply will be only when you want to resolve conflicts.
there are multiple ways :-
You can store the global version of variable , associated with a global level object like :-
var globalObject.x = "foo";
function test(){
x = "bar";
console.log(x); // it will print the local reference of x // "bar"
console.log(globalObject.x); // it will print the global level x // "foo"
}
You can use - 'this' variable
var self = this;
x = "foo";
function test(){
x = "bar";
console.log(x); // it will print the local reference of x // "bar"
console.log(self.x); // it will print the global level x // "foo"
}
var inner = function() { console.log(x); }
// test 1
(function(cb) { var x = 123; cb(); })(inner);
// test 2
(function(cb) { var x = 123; cb.apply(this); })(inner);
// test 3
(function(cb) { var x = 123; cb.bind(this)(); })(inner);
// test 4
(function(cb) { cb.bind({x: 123})(); })(inner);
All tests result in:
ReferenceError: x is not defined
Do someone know how it is possible to access 'x' as a local variable inside the callback?
Fact: when you do var inner = function() { console.log(x); } in your first line, x is not defined. Why? Because, inside your inner function, there's no local declaration of x (which would be done with var x = something). The runtime will then look up in the next scope, that is the global scope. There isn't, also, a declaration of x, so x is also not defined there.
The only places where there is a variable called x are inside each one of your 4 IIFEs following. But inside the IIFEs, each x is a different variable, in a different scope. So, if what you want is to console.log() the x defined inside each IIFE, you are taking the wrong approach.
Keep in mind that, when you define inner, you are capturing the environment inside the function's closure. It means that, whatever value could x have there (in the declaration of the function), would be the available value to the x variable later, when the inner function would be used. The fact that your x there is not defined is only an accessory, and is not what is causing the undesired behavior.
So, what happens is that when you call your inner function inside any of your IIFEs, the x referred to inside the inner function declaration is a captured value of what x had as a value when the function was defined, not the value that x has now in the scope where the function is currently being called. This is what is called lexical scope.
To solve this, you would have to pass the value that you want to console.log() inside the inner function as a parameter to the inner function, as so:
var inner = function(x) { console.log(x); }
// test 1
(function(cb) { var x = 123; cb(x); })(inner);
The only way to access the local variable x in the callback, is to pass it as an argument:
var inner = function(some_var) { console.log(some_var); }; //logs 123
(function(cb) { var x = 123; cb(x); })(inner);
OR
var inner = function(some_var) { console.log(some_var); }; //logs 123
(function(cb) { var x = 123; cb.apply(this,[x]); })(inner);
OR
var inner = function(some_var) { console.log(some_var); }; //logs 123
(function(cb) { var x = 123; cb.call(this,x); })(inner);
FURTHER
Because JS is lexically scoped, trying to reference the local variable after the anonymous function has finished executing is impossible by any other means. If you don't pass it as an argument to make it available elsewhere, JS will see it as non-reachable and it will be eligible for garbage collection.
You could redefine the callback function in the current scope:
var inner = function() { console.log(x); }
(function(cb) { var x = 123; eval('cb = ' + cb.toString()); cb(); })(inner);
// or
(function(cb) { var x = 123; eval('(' + cb.toString() + ')')(); })(inner);
This will not work if the function relies on anything in the scope in which it was originally defined or if the Javascript file has been minified. The use of eval may introduce security, performance, and code quality issues.
Have you tried using events? Emit an event inside the anonymous function, then subscribe to it in your own function somewhere else that carries out your logic.
Curious bit of code here...
var x = 5;
function fn() {
x = 10;
return;
function x() {}
}
fn();
alert(x);
Here's the jsFiddle
Is function x() {} called at all after return ?
Why not alert 10?
function x() {} is hoisted to the start of fn's scope, and this effectively makes x a local variable before x = 10; is evaluated.
The function is not set to 10.
Update: The sentence above is wrong. x is actually set to 10. var is not used to declare it, but even if it was, the last sentence in the quote below only refers to the declaration part of the name x, not its assignment to 10.
From MDN (emphasis mine):
function
Three forms with different scope behavior:
declared:
as a statement at the parent function top-level
behaves like a var binding that gets initialized to that function
initialization "hoists" to the very top of the parent function, above vars
var
function-scoped
hoist to the top of its function
redeclarations of the same name in the same scope are no-ops
function x float to the top, so you assigning the function to 10
You are declaring function x within fn; therefore, you are using the locally scoped version of x. It doesn't matter at what point the function x is declared. What you are actually doing when you set x to 10 is you are setting the function of x to 10.
This code also alerts 5:
var x = 5;
function fn() {
x = 10;
return;
var x;
}
fn();
alert(x);
The important point is that you are declaring a local variable x. Declaring a variable after the return statement also doesn't matter - it is still a local variable.
Remove the declaration and you get 10 instead because the x is no longer a local variable.
This is caused by the way variable hoisting works in JavaScript. Variable declarations are hoisted but not their assignemnts. Function declarations are also hoisted together with the function body (although function expressions are not).
So your code is effectively doing this:
var x = 5;
function fn() {
var x;
x = function () {}
x = 10;
return;
}
fn();
alert(x);
The x within the function is hence declared with only local scope and does not affect the x decalred in the main code.
This is because fn is an object, and x is a property of that object. Local-scope always takes precisence over globals.
X in fn() is a function
but x in the global scope is a var
remove the function x() {} part and x will be alerted 10
I've been told that javascript variables should all come before they are used in a function, such that:
function contrived() {
var myA, myB;
myA = 10;
myB = 20;
return myA + myB;
}
Is prefered over:
function furtherContrivance() {
var myA = 10;
var myB = 20;
return myA + myB;
}
Is this the case? And why is that?
I guess some people might prefer the former style because that's how it works inside. All local variables exist for the entire lifetime of the function, even if you use var to declare them in the middle of the function.
There's nothing wrong with declaring variables later in the function, syntax-wise, it might just be confusing as the variables will then exist before the line that declares them. Hence this function:
function bar() {
alert(foo); // Alerts "undefined". Not an error because the variable does exist.
var foo = 10;
alert(foo); // Alerts the value 10.
}
Is equivalent to this:
function bar() {
var foo;
alert(foo);
foo = 10;
alert(foo);
}
Another related fact is that nested function definitions (done using function foo() { ... }) will get moved to the top of the containing function as well, so they will be available even if the code that calls them comes before them.
Yes, the variable declaration should come at the top of the function:
function foo() {
var a, b;
}
However, initializing variables can be part of the declaration:
function foo() {
var a = 10, b = 20;
}
The reasoning behind declaring all variables at the top of the function where they are used is to avoid scope confusion.
Here is an example of bad code:
function foo() {
var b;
for (var i = 0; i < 5; i++) {
var a;
a = b = i;
setTimeout(function(){
console.log(a, b);
}, 1000);
}
}
If you execute the code, it will log 4, 4 5 times, rather than counting up. This is because only functions act as closures and introduce new scope. In JavaScript, any var declaration within a function gets executed at the beginning of the function.
This makes the above error much more visible:
function foo() {
var a, b, i;
for (i = 0; i < 5; i++) {
a = b = i;
setTimeout(function(){
console.log(a, b);
}, 1000);
}
}
There is no difference in this case between this two. I'd go with:
function furtherContrivance() {
var myA = 10,
myB = 20;
return myA + myB;
}
which is knows as single var pattern in javascript.
What you really need to take care of is defining your variables in the beginning of your functions. There is a thing in javascript called variables hoisting which means that variable definitions used in function "raise" on top. It's best described by an example:
var x = 'global'; // global (bounded to a global object which is window in browsers)
function func() {
alert(x); // undefined (you expected 'global', right?)
var x = 'local';
alert(x); // local
}
func();
what really happens is called (as I said) variables hoisting (definition of x raises on top), so the code above is actually the same as:
var x = 'global';
function func() {
var x; // definition of `x` raised on top (variables hoisting)
alert(x); // undefined in a local scope
x = 'local';
alert(x);
}
What a javscript interpreter does is it looks inside a function, gathers locally defined variables and raises them on top - this might be a good reason why you should use single var pattern.
In the example you give this is absolutely not the case. In a language like Javascript, it will be more of a developer preference, but it won't have any impact on the result.
Yes, place them at the top. It adds to code clarity.
Try this example:
var x = 1;
(function() {
x++;
alert( x ); // What will this alert show?
var x = 'done';
alert( x );
})();
Looks like it should alert 2, but it alerts NaN.
This is because the variable declaration is hoisted to the top, but the initialization stays in the same place.
So what is actually happening is:
var x = 1;
(function() {
var x;
x++;
alert( x ); // What will this alert show? NaN
x = 'done';
alert( x );
})();
...which makes the NaN expected.
For readability, it's definitely preferred.
However, Javascript "hoists" declarations. Hoisting means that vars and functions will be automatically moved to the top of their scope. This allows you to do things such as use a function before it's declared:
function myScope()
{
test();
function test()
{
//...
}
}
This can lead to some confusion, especially if variables within block scopes are declared. For example:
for(var i in foo)
{
var e = myFunc();
}
The declaration of e will be hoisted to the top of the closure, and e will be initialized to undefined. This allows for some interesting non-intuitive situations, such as:
if(!foo) //Will not throw reference error because foo is declared already
{
var foo = {};
}
So, regardless of how you declare your variables, they'll all get "moved up" to the top of the function anyway.
Hope this helps!
I thought any variable defined in a function would be local but I can easily access variable 'e' outside of its function.
function change() {
var d = 6;
e = 7;
}
change();
alert(e); //> alerts 7
Because new variables will enter the global scope by default. var prevents this from happening by constraining a variable's existence to be within the current scope.
Because it was declared without var it becomes part of the global window object.
You've not explicitly declared it as such, so it has taken global scope.
Thats because e is global by default, using var make a scope varible.
You can read more about this in Javascript Garden Scope and Namespaces
I am guessing that you are going under this assumption that
JSLint expects that a var will be
declared only once, and that it will
be declared before it is used.
Problem with your code is you are using one var, but your second line has no var in front of it. That is pushing that varaible e into the global namespace.
Why is it happening? You used a semicolon instead of a comma in the variable declaration.
function change() {
var d = 6, //Change this to a comma
e = 7;
}
change();
alert(e); //will produce an error now
It is surprisingly easy to create global variables, here are some other gotchas I've seen.
// :-( antipattern: implied global variable
function sum(x, y) {
result = x + y; // result is global
return result;
}
// :-) better
function sum(x, y) {
var result = x + y; // result is local
return result;
}
// :-( antipattern: chain assignments as part of a var declaration
function foo() {
var a = b = 0; // b is global
}
// :-) better
function foo() {
var a, b;
a = b = 0; // both local
}