Function that uses a variable defined with let on function execution - javascript

I'm wondering why I'm getting an error of "x variable not defined" inside a function that uses a variable that is declared on the same line that I execute the function. Here is the snippet (ES6)
let timeout = resetTimeout();
function resetTimeout () { timeout = 0; return timeout }
why is the scoping acting here? the variable is being defined BEFORE the function definition, so, why?
EDIT
To clarify the question, I know that it will work with var. I already read about the scoping of let and const, and I'm unable to understand why this does not works as I expect. What I'm looking for is for an explanation, not a solution.

You are creating a variable using let and immediately assigning a function's return value to it. This is okay.
The problem is inside the function. Without specifying the type of the variable timeout (= 0) you are dealing with the same timeout defined using let before. So there is a circular reference. Let's see how your code executed :
1 - First line calls the function, timeout is created in the scope but it's value undefined yet.
2 - In the function body the 'timeout' is tried to be set to 0. Since it is in the scope, js cannot create a global variable (which normally does) and assignment will throw an error.
Fix? If you intended the timeout inside the function block to be local, just rename it.
Using let is just fine.

The key concept here is "hoisting."
Every variable declared with 'var' is first hoisted, then it will be assigned; With 'let' it is different, it will not hoist the variable, so first js will evaluale the right side of the assigment; at that point the variable is not defined yet!
The next, slightly different code works with let:
let timeout
timeout = resetTimeout()
function resetTimeout () { timeout = 0; return timeout }
Note: A variable declared by let or const has a so-called temporal dead zone (TDZ) (see http://2ality.com/2015/02/es6-scoping.html#the_global_object)

Taking into consideration your update and this guy's answer, I would like to state that you don't need to rename your variable. Since let works only on one layer and do not go deeper into scope, you can declare new variable timeout within the function.
let timeout = resetTimeout();
function resetTimeout () { let timeout = 0; return timeout; }
Hope that helped a bit. Sorry for previous answer.

Related

Can we access a variable declared using 'var' keyword inside a block? [duplicate]

This question already has answers here:
How JS hoisting works within functions?
(4 answers)
Closed 2 months ago.
A variable is declared using 'var' keyword inside a block ( {...} ). Can we access that variable outside the block?
I searched on several websites and the answer was 'yes'. 'Yes we can access the variable' it said.
But when I executed this on a browser console it said "the variable is not defined". Any thoughts why this is so?
Here's the screenshot of the same
I expected it to give 12345.
A variable is declared using 'var' keyword inside a block ( {...} ). Can we access that variable outside the block? I searched on several websites and the answer was 'yes'.
tldr; It's more precise to say Yes, unless that block is a function body.
var scopes a variable to the nearest function.
The body of the function (excepting some arrow functions) is a block.
Not all blocks are associated with functions.
{
var inBlock = 1;
}
console.log(inBlock); // This logs 1
function aFunction() {
var inFunction = 2;
}
aFunction();
console.log(inFunction); // This triggers a reference error
You can access the variable but that doesn't necessarily mean your variable will actually have a value before passing through said block.
var will be known throughout the entire function. So if you declare var x at the end of your function (or in an if-block nested in a while in another while) and assign it there, you will be able to call it at the start of the function, but the value will be "undefined" since it hasn't been assigned a value yet at that point of the execution.
Hence why var needs careful consideration on usage to make sure you actually need said variable outside the block where you're using/initializing it.
for-loops are (in my opinion) easiest to explain this with:
for( var i = 0; i < arr.length; i++ ) {
// do something
};
The variable i here will actually be known OUTSIDE the for-loop. It will be undefined before going through the for loop and will have the value of the last iteration after the for loop. (As oppposed to using let which will make it so variable i is not known outside the for-loop)
Variables declared with var has global and function scope but does not have block scope. Which means these variables are only accessible inside a function or globally.
// if we try to access it from function scope, it is not accessible
function greeting() {
var sayHi = "Hello"
return sayHi
}
console.log(sayHi) // sayHi is undefined. Because it has function scope
// if we try to access it from block scope, it is accessible.
if (1 < 2) {
var sayBye = "Bye"
}
console.log(sayBye) // prints 'Bye', because it has no block scope
After execution of the function, the variable will no longer be available and it will be referencing the globally scope which don't have your variable.

Not understanding the concept of local and global variables

Ok so just a quick one... I REAAAAAALLLLY am not understanding scope at all. Do some reason whenever I put a variable inside of a function it's undefined. It seems the only way it works it to declare it OUTSIDE of a function. So what gives? Am I simply misunderstanding the concept of local and global variables? Because it seems global variables are the only ones that work...
Edit:
Simple example...
let a = 'something';
function myFunction() {
// Code block
};
This works 👆
But the following gives me undefined variables:
function myFunction() {
let a = 'something';
// Code block
};
First lets take a look at the differences between local and global variables.
LOCAL VARIABLES - Any variable defined inside of a function or loop. If the variable is defined in a certain function/loop, it can only be accessed in that function/loop.
For example:
function number() {
let a = 0;
// Can access a here.
}
// Cannot access a here.
GLOBAL VARIABLES - Any variable that is not defined inside of any function or loop. These variables can be accessed anywhere.
For example:
let b = 0;
function number() {
// b can be accessed here.
}
// b can be accessed here.
The scope of a variable is where it can be accessed in. For ex, a scope of a local variable that is defined inside of function number() is the number() function since it can only be accessed there.
One more quick thing (Credit to Mikael Lennholm) var doesn't scope a variable to a loop (or any non-function statement block), it only scopes it to the current function. To scope a variable to any statement block you have to use let/const
Now let's look at your examples:
Here is the first example:
let a = 'something';
myFunction();
function myFunction() {
console.log(a);
};
That works fine, and that's what you said.
Here is the second example you said doesn't work:
function myFunction() {
let a = 'something';
console.log(a);
};
myFunction();
But, if you click the run button, you'll actually see it does work, it does get logged. If you after this go and check in the console what the variable a is of course it will be undefined, since a can be only accessed in the myFunction() function, and the console is not in that function.
You said that the script doesn't work, it's most probably because of something else (if it was because of this there would be an error in the console). You can try posting the website here and I can help you.
Hope this helped you :)
Global variables are variables that you have access to them anywhere in your codes.
Local variables are limited to their scopes like a function, module, class and etc.
if you define a local variable it can be accessible in the nested level like closures.
if you define a global variable and you define the same name as local variable , the local overwrite the global, then be careful.

Closure of function defined as an argument

Disclaimer: This question is purely curiosity driven and has to do a lot with how the javascript works.
I understand why the following code works. Due to closures, foo has access to the scope where a resides. This makes sense.
var a = 10
var foo = function(){
console.log(a);
}
setTimeout(foo,1000)
However, i wonder why the following also works (explained right after).
var a = 10
setTimeout(function(){
console.log(a);
},1000)
The function is defined in the argument of the function receiving it and essentially was never a closure to the scope that contains a. We know that when a function receives an argument, it creates a local variable for that argument so for example
var outerVar="5"
var bar = function(a){
//implicitly, var a = outerVar happens here
console.log(a)
}
bar(something);
So following that logic, the function passed to setTimeout couldnt have access to a and yet it does.
Im suspecting that when a function is defined in the argument space what happens is, it realy is defined before being assigned as an argument but have no proof of that. Any pointers highly appreciated.
Thanks a bunch.
It's not exactly closure, but it's close.
Strictly speaking, closure is when a variable's scope ends, but is still enclosed in an inner function that still lives on:
function createTimer() {
let counter = 0;
return function() {
return counter++;
}
}
const timer = createTimer(); // function() { ... }
console.log(timer(), timer(), timer()); // 0, 1, 2
The function in which counter is defined has returned, the scope ended, and under normal circumstances, counter should have died and garbage collected. But the inner function returned from createTimer() still has a reference to it, from the enclosed scope, that is a closure.
In JavaScript, every function has access to all of the scopes of all of its ancestors, this is what you're seeing here.
The function passed to setTimeout() has access to a because a is defined in the scope around it.
When you look at a javascript code and see how it works, the best way according to me is first understand the javascript engine how it works.
First it traverses the code and assigns all the variables to the scope and further more in the second trace it assigns the values based on the scopes.
So in your first code,
The engine first traverses and assigns
var a to global scope,
var foo as global scope,
Then when setTimeout runs it calls foo function and logs the value of a as that of the global a as it doesnt have any local “a” so checks the lexical scoping.
In your second code,
Var a is again global scoped
and no other declaration this time.
In second traverse it assigns the value 10 to a and interprets the settimeout and prints the value
In your third code,
Same as the second one except the fact that instead what “foo” was giving to the settimeout function, you wrote your callback function then n there itself.
By the time l it executed the setTimeout,
Each of your codes have the value for “a” in the global scope that they are accessing.

Access variable in if block

var func = function () {
var i: number = 0;
if (i == 0) {
var y: number = 1;
}
document.body.innerHTML = y.toString(); // js/ts should complain me in this line
};
func(); // output: 1
As you can see, I've declared variable y inside if block. So, I think it couldn't be referenced outside the scope.
But, when I've tried to run the code, the output is 1.
Is it an issue in typescript/javascript?
Variables in Javascript are hoisted, meaning that var y is moved to the top of the function as if it were declared there.
Initialization, however is not hoisted, so if you change i to be something other than 0, the variable y will still exist, but it will have the value undefined.
This means that the function is exactly equivalent to:
var func = function () {
var i: number = 0;
var y: number;
if (i == 0) {
y = 1;
}
document.body.innerHTML = y.toString(); // js/ts should complain me in this line
};
To get the behavior you expect, you need to use let, which is a part of ECMAScript 2015 (ES6). It is block scoped, as you expect. It will also effectively work so that it is accessible only from the point of definition onwards, which is also probably as you would expect.
If you re-declare a JavaScript variable, it will not lose its value.
The second reference might pave way for a new variable syntax. Actually if you recall variable declaration is not neccessary in javascript. Simpe
y=1;
also works.
The second time when you reference y, outside if block, in my opinion, it tries a re-declaration and retains the old value.
Reference - http://www.w3schools.com/js/js_variables.asp
& http://www.w3schools.com/js/js_scope.asp
Javascript has function scope afaik. Any variable declared within a function, should be accessible from anywhere within the function. So, if you have a function checking if i==0, then you can achieve what you are trying to achieve.
This is as it is supposed to be. Javascript scopes by function, not by block. (This does make it unlike many popular languages)
Variables declared like var myVar = 10; will seep into nested functions but not the other way around. Variables declared like myVar = 10; will go global.
I couldn't find anything which suggested that typescript was any different.
Variables declared inside of an if statement are not scoped to the if statement. They're scoped to the current execution context. There's the Global execution context and then when a function is run, it creates it's own execution context. Inside of your function, you created the variables y and i. It doesn't matter that y was created inside of the if statement, because once it runs, y is created in the scope of the function. So then you do y.toString(), which can access y because it's scoped to the function not the if statement. That's why you get the output of 1. This is not an error, it's by design.

JavaScript Lexical Scoping and Life of Variable

I was curious why this works:
function doThis(){
counter = 0;
return counter;
};
console.log(counter); // returns "reference error: can't find variable"
which makes sense, as the variable does not exist outside of the function. But if I make a function that self executes:
(function doThis(){
counter = 0;
return counter;
})();
console.log(counter); // returns 0
How come the variable counter still exists? It's not a closure, there's nothing that seems to be referencing this variable from the outside, so shouldn't it be destroyed by garbage collection?
You are creating it as a global as you haven't included var before the variable name.
The function in the first example hasn't been invoked so the variable has not been created yet, in the second one it has so that is why you get 0
What your code should be doing is:
function doThis(){
var counter = 0;
return counter;
};
First edit them so that it is instantly clear what's happening (no leaving out var hacks):
function doThis(){
window.counter = 0;
return counter;
};
console.log(window.counter); // returns undefind
And:
(function doThis(){
window.counter = 0;
return counter;
})();
console.log(window.counter); // returns 0
Can you now see what's happening? The function defines a global variable so of course it's not available until the function is called. window refers to the [object global] in browsers.
This is the reason you always want to use either global.something OR var something, so that it's very clear to anyone whether you intend to use global or local variable. If you used var in the OP, the variable would be local.
Since you are not declaring it with "var" it gets assigned to the global scope which is visible out side of the function. In the first example you are not executing the function so the counter is never defined where as in the second example you invoke the function and counter gets assigned to the global scope
In the first example you haven't called the function and so counter doesn't exist yet (because the code inside the function hasn't executed yet). In the second example, you have defined a function literal and you are self-invoking it. The code inside the function execute and counter is now defined.
Furthermore counter is a global variable because you haven't defined it using var and so it is visible to the scope outside the function. It's the same as doing window.counter = 0.
Now if you had done the following:
(function doThis(){
var counter = 0; //notice the var
return counter;
})();
counter would still be undefined because it is local to the scope of the function.
So to recap:
In the first example counter is undefined because the code hasn't run yet. If you actually call the function, you will get the same behavior as the second example.
In the second example counter is defined and is a global variable (basically the same as window.counter) and it is defined because the code inside the function was executed when you defined it and self-invoked it.
In the third example counter is unknown to the global scope because it is local to the function inside which it was defined (because var was used).
As you know, in JavaScript, if you declare a variable without "var" keyword, it will be added to the global scope(window object).
But if a variable is declared within a function without using "var" keyword, it won't be added to the global scope(window object) until that particular function is invoked.
In JavaScript, you have to understand about execution context creation.
when the complete JS file is interpreted, the the memory is allocated for all the functions and variables(hoisting).
Please note that all the variables which are declared outside of the functions are added to the global scope(window object).
Execution phase:
As JS code execution is synchronous(only one line at a time) and single threaded, an execution stack is created and the "global execution context" is pushed to the execution stack.
If a function invocation is encountered in the execution, "function execution context" is created for the corresponding function and pushed to the same stack.
Now during function execution, JS engine parse the code and if it come across a variable, the memory is allocated and if the variable is declared without "var" keyword, it will add this particular variable to the global scope. otherwise that particular variable will be part of function scope(local scope).
Now let's check your code snippets:
function doThis(){
counter = 0;
return counter;
};
console.log(counter); // returns "reference error: can't find variable"
in this example, as the doThis() function is never executed, variable "counter " is not allocated any memory and not part of any scope(global/local). hence in the console log, you see a reference error.
(function doThis(){
counter = 0;
return counter;
})();
console.log(counter); // returns 0
As its a self invoking function, "counter" variable will be allocated memory and moved to the global scope(window object). hence you can see the value of the "counter" variable in the console log.

Categories

Resources