How is this valid javascript, and what purpose does it serve? - javascript

I was answering a question on quora and encountered something like following:
// if(true)
{
var qq = 1;
}
Immediately I fired up chrome and tried the following
{
var qq = 1;
}
To my surprise no syntax errors. I thought this might be a way to set up a closure without a function, but alas nothing like that is true.
Why is this valid JavaScript? Does this serve some purpose? Are there any issues due to this?

The blocks are used just for statements like if(){}, function(){}, switch(){}, for(..){}, etc.
In this example:
var number = 0;
if(true)
number = 1
else
number = 2
number = 3 //This line is evaluated because no longer is in the `else` condition
console.log(number); //3
Using blocks this will not happen:
var number = 0;
if(true)
{
number = 1
}
else
{
number = 2
number = 3
}
console.log(number); //1
Using blocks without using any statement are unnecessary, eg:
var foo = 2;
foo = 3;
console.log(foo) //3
It is the same as doing:
var foo = 2;
{
foo = 3;
}
console.log(foo) //3
Except if you are developing in an environment of ES6. Using let declarations in blocks are very important. And these will be evaluated only within the block.
let foo = 2;
{
let foo = 3;
}
console.log(foo) //2

It is a block.
Syntactically, a block is just a bunch of statements that are grouped together.
Block : { StatementList }
StatementList
: StatementListItem
| StatementList StatementListItem
StatementListItem
: Statement
| Declaration
But here's the tricky part: blocks are statements, too.
Statement : BlockStatement
BlockStatement : Block
And there you have it, now you can define compound statements such as if or while in terms of an abstract Statement, without having to worry about whether it is a single statement or a block.
IfStatement
: if ( Expression ) Statement else Statement
| if ( Expression ) Statement
IterationStatement
: while ( Expression ) Statement
| for ( LeftHandSideExpression in Expression ) Statement
| for ( LeftHandSideExpression of AssignmentExpression ) Statement
Isn't it beautiful?
As a side effect of this language design, blocks can contain other blocks inside — it isn't worth prohibiting this quirk explicitly in the language specification, although semantically in EcmaScript 5.1 inner blocks were indeed superfluous and very rarely used.
{
var x = 1;
{
var x = 2; // The same variable as in the outer block.
console.log(x); //=> 2
}
console.log(x); //=> 2
}
In the latest EcmaScript 6 (2015) standard, however, such blocks do have semantic value, as shown in the next example:
{
let x = 1;
{
let x = 2; // New variable, visible in this block only.
console.log(x); //=> 2
}
console.log(x); //=> 1
}
let and const are block-scoped variable declarations introduced by ES6.

Related

var hoisting and value between redeclaration

Run below code in Node environment. Running it in browser console doesn't allow to redeclare variable of var.
console.log(a);
var a = 5;
According to hoisting, the above code is going to look like this
var a = undefined;
console.log(a); // undefined
a = 5;
a variable is being hoisted to the top of the file. JS Engine allocates memory for this variable before the execution. The question is why below code consols 5 instead of undefined.
var a = 5;
console.log(a);
var a = 6;
I'm looking at this code and imagining that it's going to look like this:
var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
I'd like to be sure of the answer instead of guessing. The JS Engine is smart enough to see that a variable is already declared and is going to ignore the next var expression and rehoisting in such case? So the output should be looking like:
var a = 5;
console.log(a); // 5
a = 6;
So it's like:
JS Engine sees for the first time declaration (in this case along with initialization) of a variable so it's allocating memory.
JS Engine sees for the second time declaration of a variable but is going to ignore the hoisting because variable of given name is already in the memory.
Am I wrong in something?
Preface: In modern JavaScript, var should never be used. Use let or const.
The JavaScript engine handles var in two steps:
Upon entering the global scope or a function scope, it processes every var in the entire scope, defining variables for the them initialized wit the value undefined. If a variable is declared more than once with var, it's exactly as though it were declared once.
Then it starts the step-by-step execution of the code. In that step-by-step execution, any initializer on a var statement (the = 5 in var a = 5) is considered an assignment. So var a = 5 is treated exactly like a = 5 at this point.
So in your example:
var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
It's as though you had written this:
var a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
a = undefined;
console.log(a); // undefined
var a = 6;
or this:
var a;
a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
var a;
or even this (but please don't! :-) ):
var a = 5;
var a = undefined;
console.log(a); // undefined
var a = 6;
var a;
That is, first all the variables declared with var are created (and only once), then the code runs as though any initializers on them were assignments.
This is not how let, const, and class declarations are handled (collectively: lexically-scoped declarations). First: Multiple declarations in the same scope are an error (including if one of them is with var and the other is with one of the lexically-scoped ones). Second: They're hoisted (in Step 1 above), but the hoisted binding¹ is uninitialized until the declaration is executed in the step-by-step code, at which point it's initialized (either with the value from the initializer, or with undefined if it's just e.g. let a;). The time between entry to the scope and the point the binding is initialized is called the Temporal Dead Zone. var doesn't have it because var variables are initialized when they're created (with the value undefined), but let, const, and class declarations do.
¹ The term binding is the general term for variable-like things. In the code:
function example(p) {
var v;
let l;
const c = 42;
function f() {
}
class C {}
}
the bindings created upon entering the example function's scope are p (a parameter), v (a var variable), l (a let variable), c (a const constant), f (the binding created by a function declaration), and C (the binding created by a class declaration). (Note: function and class expressions are handled slightly differently.)

why does var behave differently in a with statement depending on whether or not the passed object has a property with the same name?

function foo(obj) {
with(obj) {
var x = 2;
}
console.log(x);
}
let o1 = {};
foo(o1); // 2 (x is visible even outside the with statement because of *var*)
function foo2(obj) {
with(obj) {
var x = 2;
}
console.log(x);
}
let o2 = {
x: 1
};
foo2(o2); // undefined (why?)
I'm reading along Kyle Simpson's Scope & Closures book from the YDKJS series and I was able to understand all of the quirks of with statements and how with(obj) { a = 1 } is functionally different from obj.a = 1 despite having been intended as a shorthand for it in cases of long object names and having to constantly reference it. This is because the object's properties are treated as lexically defined identifiers in that scope (a vs obj.a), and that in sloppy mode, one side effect of this is that if the object you pass into a with statement doesn't have a property that you are trying to assign to, a global variable of that name will be created. Still, armed with all this knowledge and more, I don't quite understand why the code above behaves the way it does. Why does foo(o2) log undefined?
The difference in behaviour can be accounted for by this behaviour, described in (for instance) the following note in ECMAScript 2022 Language Specification sect 14.3.2.1:
NOTE: If a VariableDeclaration is nested within a with statement and the BindingIdentifier in the VariableDeclaration is the same as a property name of the binding object of the with statement's object Environment Record, then step 5 will assign value to the property instead of assigning to the VariableEnvironment binding of the Identifier.
In the first case:
function foo(obj) {
with(obj) {
var x = 2;
}
console.log(x);
}
let o1 = {};
foo(o1);
because obj has no x property, the var statement is hoisted to the top of the function and is therefore visible beyond the scope of the with statement.
In the second case:
function foo2(obj) {
with(obj) {
var x = 2;
}
console.log(x);
}
let o2 = {
x: 1
};
foo2(o2);
x exists on obj, so we satisfy the conditions laid out in the quoted note, and therefore the value is assigned to the property and no hoisted variable is created. Now, there's no x outside the scope of the with statement.

Assign result of block to a variable (gives SyntaxError)

If I open a Node REPL, and enter this code, I can see that it evaluates to 3:
$ node
> { let a = 1 + 2; a }
3
>
However, this code does not work:
const result = { let a = 1 + 2; a };
I get this error:
const result = { let a = 1 + 2; a };
^
SyntaxError: Unexpected identifier
What am I missing here? Am I correct in assuming that the block evaluates to the last expression within it, or is Node's REPL misleading me here?
As you've seen in the Node REPL, a block evaluates to a value, and that value is usually the value of the last statement in the block.
In ECMAScript 6.0, a BlockStatement is defined as follows (with subscripts omitted for simplicity):
BlockStatement:
Block
Block:
{ StatementList }
StatementList:
StatementListItem
StatementList StatementListItem
StatementListItem:
Statement
Declaration
Per section 13.2.13, Note 2, "The value of a StatementList is the value of the last value producing item in the StatementList." A Block is evaluated to the value of its StatementList, and a BlockStatement is evaluated to the value of its Block.
Thus, Node is correctly evaluating your BlockStatement to the value of the last value producing item, which is a. This is not a bug, nor is it specific to Node.
The reason you are getting an error is because you are trying to use a BlockStatement in a context where it is not allowed.
const result = { let a = 1 + 2; a }; is a LexicalDeclaration, which is defined as follows (again, with subscripts omitted):
LexicalDeclaration:
LetOrConst BindingList ;
LetOrConst :
let
const
BindingList:
LexicalBinding
BindingList , LexicalBinding
LexicalBinding:
BindingIdentifier Initializer
BindingPattern Initializer
We also need the definition of Initializer:
Initializer:
= AssignmentExpression
As you can see, the part of your lexical declaration after the equal sign requires an AssignmentExpression. If you look through the grammar for expressions, you will see that BlockStatement is not an AssignmentExpression.
As of ES6.0, there is no way to use a BlockStatement direcly as an AssignmentExpression. However, if you want to evaluate one to an expression, you can use eval:
> const result = eval("{ let a = 1 + 2; a }");
> result
3
I don't recommend doing this under ordinary circumstances, but it is helpful for seeing how ES6.0 does indeed evaluate blocks to values.
In js braces represent an object which expects key: value pairs. You can get the result like this;
const result = {a: 1 + 2};
console.log(result.a);

Variable scope Javascript [duplicate]

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

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