Variable scope Javascript [duplicate] - javascript

This question already has answers here:
Are variables declared with let or const hoisted?
(7 answers)
Closed 4 years ago.
var x = true;
if (x == true) {
console.log(typeof(x));
let x = false;
};
console.log(x);
Sorry new to programming and getting this error:
Error: x is not defined.
I am extremely confused.

It doesn't look like it, but the two variables you declared are different. Your var declaration is one of them, and the let declaration is different. You've confused yourself by naming them with the same variable.
The let x exists strictly within the if block. This let x is the only x that can exist in that block now and you've tried to use that x before you declared it when you attempt a console.log.
You can get away with using variables before you declare them with var, but let and const won't let you.
Also, I highly recommend you don't mix var and let in your code. You have to really understand what's going on if you do.
var x = true; // first x variable
if (x == true){ // first x variable
console.log(typeof(x)); // second x variable, used before declaration
let x = false; // second x variable created in `if` block
};
console.log(x); // first x variable
edit:
Your follow up question is still 2 different variables. The only thing you've done is coerced the code to make it look EVEN MORE like the same variable.
var x = true; // first x
if (x == true) { // first x
let x = false; // second x
console.log(x); // second x, false
};
console.log(x) // first x, true

This one's a little tricky to explain, but I'll try;
var x = true;
if (x == true) {
console.log(typeof(x)); // <-- Error: x is not defined.
let x = false;
};
console.log(x); // true
The last line is actually fine because you defined x up top.
What's happening inside the if statement is that JavaScript looks ahead and sees that you've declared x (with let or const) somewhere in that scope, but it hasn't yet reached that line.
So merely having a let or const somewhere further down in the same scope is causing that variable to not be defined.
If you move it up a line, everything is fine:
var x = true;
if (x == true) {
let x = false;
console.log(x); // false
};
console.log(x); // true
And if you don't redeclare x, it's also fine:
var x = true;
if (x == true) {
let y = false; // different variable name (won't "shadow" the `x` from above)
console.log(x,y); // true false
};
console.log(x); // true
var behaves differently:
if (true) {
console.log(y); // undefined
var y = false; // `var` is less strict. The declaration of `y` is hoisted to the top of the scope, but it's not assigned until this line
console.log(y); // false
};

The part of explanation that everyone is missing is that declarations are hoisted, which means that the declaration is brought to the beginning.
This is especially true with var. For let things are a little bit different and the term hoisting is not the most accurate. But it is useful to understand what is going on.
When executed, your code will look something like this (not exactly but gives the idea)
var x = true;
if (x == true) {
let x // hoisted at top of scope, for let scope is this if block
console.log(typeof(x));
x = false;
};
console.log(x);
Now everything should make sense: typeof(x) refers to the x declared with let but not yet initialized.
To be clearer this is the reason why the x inside of typeof refers to the x declared in the row below and not the one at the beginning: because the declaration of the existence of x inside that block is brough to the beginning of such block

Related

JavaScript - Is it okay to define a property of a previously declared object during a variable declaration?

Is it common practice (or at least syntactically valid across browsers and implementations of JS) to abuse RTL associativity of assignment operators so that one can define two variables (with the first being an object) so that the second is assigned to a (newly) named property of that object which is itself assigned to another value, so that a SyntaxError() is not generated?
I know that sounds complicated, but here is the code:
var x = {}, y = x.l = 9; // generates no errors
console.log(x.l, y); // 9 9
Since:
var t = {}, t.l = 9; // Syntax Error, no doubt because t is already defined
The line:
var x = {}, y = x.l = 9;
effectively becomes:
var x = {};
var y = x.l = 9;
which is processed as:
// Parse phase
var x; // assigned the value undefined
var y; // assigned the value undefined
// Execution phase
x = {};
x.l = 9;
y = x.l;
Noting that in an assignment expression, the value on the right is assigned to the expression on the left. Compound assignments are evaluated left to right, but assigned from right to left, hence x.l = 9 is assigned before y = x.l, even though it's on the right.
Now try that with the second example:
var t = {}, t.l = 9;
becomes:
// Parse phase
var t; // assigned the value undefined
var t.l; // syntax error, stop
The var keyword at the start of a statement means the next thing must be a valid identifier. t.l is not a valid identifier (it can only be interpreted as an identifier followed by a dot property accessor), so that's it. Everything stops.
You need to use the semicolon - that's what the error says the parser expects:
var t = {};
t.l = 9;
console.log(t);

What is the use of just declaring variables

I have been using python for a while now, and have just started learning javascript. In javascript you can, as I understand it, declare a variable without assigning a value to it (var cheese compared to var cheese = 4) in what situation would you want to declare a variable but not assign a value to it straight away?
Consider this snippet.
if (someCondition) {
var x = 5;
} else if (someOtherCondition) {
var x = 4;
}
if (x) {
doFunc();
}
Since x needs to exist for the doFunc to run, you simply add an undefined declaration above. var x; so that the if (x) doesn't return an error.
You do this when you want the value of the variable to be undefined.
var cheese;
console.log(cheese); // undefined
It's simpler than
var cheese = undefined;
The undefined value doesn't seem much useful, but this will allow to assign some other value later.
var cheese; can be perfectly useful (even if you never assign anything to it). Of course it's a shorter way to type var cheese = undefined;, but that's not the only reason…
Using var declares a local variable, and this has a nice property: it hides variables from parent scopes.
There's another part to your question:
If we're going to assign a value to var cheese anyway: why not assign immediately?.
Answer: it may be fine for your algorithm to return cheese without ever assigning anything to it — i.e. "undefined is valid".
Here's an example which illustrates how var hides variables from parent scopes:
var a = 3;
console.log(a); // prints 3; "a" is defined in this scope
function findEvenNumber(numbers) {
var a; // we declare this local variable, to ensure that we do _not_ refer to the variable that exists in the parent scope
numbers.forEach(function(number) {
if (number % 2 === 0) {
a = number;
}
});
return a; // if no even number was found, it returns undefined (because we never assigned anything to a)
}
findEvenNumber([1, 2]); // returns 2;
console.log(a); // prints 3; the "a" in this scope did not get overwritten by the function
Speculation: maybe the var cheese; syntax exists in ECMA to enable programmers to declare all variables at the beginning of their function. Such a convention was enforced by the C89 compiler, and some people grew fond of it.

JavaScript, changing a variables value inside of a function when the variable is passed as a parameter [duplicate]

This question already has answers here:
Does JavaScript pass by reference? [duplicate]
(13 answers)
Closed 7 years ago.
Why doesn't this work and is there any way to get it to work?
Im pretty sure it doesn't work because of variable scoping in javascript, but I can't think of any ways to get it to work.
Code
var x = 5;
var z = function(y) { y = 10; };
z(x);
console.log(x) // Outputs 5 instead of 10
var x = 5
var z = function(y) { y = 10; }
console.log(x) // Outputs 5 instead of 10
Where exactly did you changed X? you dont have X and you did not call the function as well.
I assume you wish to do this:
var x = {x: 5}
var z = function(y) { y.x = 10; }
z(x);
console.log(x.x) // Outputs 10
The code is using x as variable on an object instead of a primitive number which is passed by value and not by reference and thus cannot be modified.
Using object you can modify the attributes (properties) of the object
X won't change for a couple reasons. The first is that you are modifying the parameter y inside of the function. If you passed an object this would work as those are passes by reference. It would also work if you changed x directly instead of passing it as an argument.
The second reason is that you never ran the function. You need to call z with z()
What happens when you invoke the function z is that the parameters get initialized and assigned the value you passed in.
So the function might read something like
var y = 5;
y = 10;
and so, the y that is in the scope outside of the function, remains unchanged because this y is actually a whole new y variable just for this function's scope.
var x = 5;
var z = function(y)
{
y = 10;
//assigned value 10 to the function parameter variable y.
//You haven't done x=y or x=10 and so how do you expect the value of x to change?
}
console.log(x) // Outputs 5 instead of 10
Change it to:
var x = 5 ;
var z = function(y)
{
x = 10; //changed value of x to 10
}
console.log(x) // Outputs 10
The problem is not related to vatiable scope at all.Your variable x is global since it is outside the function.
AND YES,you can change it's value inside a function.But the problem with your code was that you never changed the value of x.
If this is what you intended to do:
z(x); //x=5 is passed to the function z
function(y)
{
y=10; //initially the value of y is 5.Since you have passed x.
//Doing y=10 does not change the value of x because y is another variable.The value of x was copied to y and y is not x.
x=y;//You have assigned 10 to x
}
console.log(x); //outputs 10
Changing the value of the function parameter does not change the value of the variable whose value you passed to the function.You are actually passing the value of x to y that is you are passing 5 to y and not the variable itself.
You are not passing the variable reference.You are just passing the value.

Explanation of how is the variable "leaking"?

I was reading about hoisting in mozilla, and noticed an example explaining how a variable can leak outside of the function scope:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#Initialization_of_several_variables
The example says,
var x = 0;
function f(){
var x = y = 1; // x is declared locally. y is not!
}
f();
console.log(x, y); // 0, 1
// x is the global one as expected
// y leaked outside of the function, though!
I don't understand what is happening here. I am looking for a technical explanation more than anything. How is y accessible outside at this point?
Edit: I understand how this function is behaving, and I should clarify and say I wanted an understanding of what is happening in the code and memory (ex, pointers, etc..).
you have var x = y = 1 which means that x is declared inside but y is not, var doesn't apply to y in this case, that's why it's leaked outside the function
This is breakdown of the statement:
var x = y = 1;
\___/ \____/
identifier initializer
\____________/
variable declaration
In this case the initializer is another assignment. Due to variable hoisting, this statement is evaluated as follows:
var x;
x = y = 1;
As you can see, y is not declared. Assigning to an undeclared variable creates a global variable (implicitly).
var x = 0;
function f(){
var y = 1;
var x = 1;
}
f();
console.log(x, y);
Try the above code. The problem is that you didn't include a var in front of y. The result is that y is hoisted into global scope.
In JavaScript, variables are declared as soon as you use them, like this:
function f(){
y = 1;
}
The problem is that y is not scoped within f(), like it would be in most other programming languages. So you can do something like this:
function f(){
y = 1;
}
console.log(y);
And you will see 1 on the console.
This is generally considered a bad practice, because it pollutes your namespace and can lead to subtle, hard to track down bugs.
To avoid this, always use let or var to explicitly declare variables, like this:
function f(){
let y = 1;
}
// the y declared in f() is not accessible out here
console.log(y)
This should give you an error like: "Uncaught ReferenceError: y is not defined" on your console.
For more details, read this page, specifically the section 'Automatically Global': http://www.w3schools.com/js/js_scope.asp
In the example you gave above the problem is more subtle, because x is scoped properly, but the short-hand declaration of y makes it globally scoped:
function f(){
let x = y = 1;
}
EDIT 2022: Updated to recommend let over var. Here is an explanation of the difference.

declaring a variable within conditional expressions (ternary operator)

Is it possible to declare the variable within a conditional expression?
for example:
The code below return a syntax error (because I've declared the variable x within the conditional expression?).
var a = document.getElementById("userData");
var d = a.value;
function() {
(d.length>15)?(
alert("your input was too long")):(
var x = parseInt(d).toString(2),
a.value=x
);
}
obviously this can be fixed by simply adding var x; outside the statement, but is it possible for variables to be declared here?
Is it possible to declare the variable within a conditional expression?
No. var is a statement, and the operands to a conditional expression are expressions. The language grammar doesn't allow it. Thankfully.
You can do this with an immediately-invoked function:
(d.length>15)?(
alert("your input was too long")):
(function(){
var x = parseInt(d).toString(2);
a.value=x;
}())
);
But note that the x variable will not exist outside of the inner function. (I can't tell whether you want it to exist after the expression is evaluated or not.)
No. But you can initialize it with undefined
and set it with condition.
function Test()
{
d = 25.6654;
var x = (d.toString().length > 15) ? parseInt(d).toString() : undefined;
alert(typeof x === "undefined");
}
Then you can work with if(typeof x == "undefined") //do something

Categories