Assign result of block to a variable (gives SyntaxError) - javascript

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);

Related

Puzzled with the workings of these variable assignments?

Tried to figure out the working logic behind the results. I couldn't.
Could someone please help explain how Javascript works the output of each of the 4 situations and the reason behind it?
I think the 1st and 2nd are similar situations.
Thanks
var num = (1, 2, 3); // num = 3 (last number returned, No error generated)
console.log(num)
y = (1, 2, 3); // y = 3 (last number returned, No error generated)
console.log(y)
z = 1, 2, 3; // z = 1 (first number returned, No error generated)
console.log(z)
// Uncomment the following to run: (gives an error)
//var m = 1, 2, 3; // Error: Unexpected number
Your first three snippets are all basically the same. They demonstrate usage of the Comma Operator.
The Comma Operator a, b first evaluates a and throws away the result, then evaluates b and returns the result. It is left-associative, so a, b, c is the same as (a, b), c.
Observe:
const foo = () => { console.log("foo"); return 23; };
const bar = () => { console.log("bar"); return 42; };
const qux = () => { console.log("qux"); return 99; };
console.log((foo(), bar()));
// foo
// bar
// 42
console.log((foo(), bar(), qux()));
// foo
// bar
// qux
// 99
Note that I could have written the three functions like this, but of course it doesn't make much sense to use the Comma Operator while trying to explain it:
const foo = () => (console.log("foo"), 23);
const bar = () => (console.log("bar"), 42);
const qux = () => (console.log("qux"), 99);
console.log((foo(), bar(), qux()));
// foo
// bar
// qux
// 99
In your first snippet, you have a Variable Declaration statement, which declares a variable, evaluates the initializer, and binds the value of the initializer to the variable. The initializer in this case is 1, 2, 3, which is equivalent to (1, 2), 3. So, it will evaluate 1, throw away the result, evaluate 2 return the result 2 as the result of evaluating the sub-expression 1, 2 but then immediately throw it away, evaluate 3 and return the result 3 as the result of the evaluation.
Therefore, it binds the value 3 to the variable num.
The second snippet is exactly the same, except it is an Assignment Expression, not a Variable Declaration, i.e. in Strict Mode, it will assign to a variable named y that was previously declared with let or var and error out with a static error otherwise. In Loose Mode, it will assign to a variable named y that was previously declared with let or var, or to a property named y of the global implicit object otherwise.
The evaluation of the right-hand-side of the assignment is exactly the same as above.
Snippet #3 uses again the Comma Operator. The Comma Operator has higher precedence than the Assignment Operator (the way ECMAScript is specified, it does not contain a simple operator precedence table, rather, the precedence is implicit in the grammar, so you'll either have to read the entire grammar or believe me), so your snippet #3 is equivalent to
((z = 1), 2), 3;
Again, it works just the same as snippets #1 and #2: first, the expression z = 1 is evaluated (which has the side-effect of assigning 1 to z) and the result is thrown away, then the expression 2 is evaluated and the result is thrown away, then the expression 3 is evaluated, and the result is returned.
So, the result of the entire expression is 3, but since that result is not assigned to anything, not returned, not printed, it is simply thrown away. All that is left is the side-effect that happened while evaluating the first expression, namely assigning 1 to z.
The last expression is different, because it does not demonstrate the use of the Comma Operator. It is, again, a Variable Declaration, just like snippet #1. A Variable Declaration basically is a list of comma-separated identifiers with optional initializers. The reason you get an error here, is that 2 is not a valid identifier for a variable.
Observe:
var n = 1, o, p;
// Declares variables `n`, `o`, `p`, initializes `n` to `1`.
console.log(n, o, p);
// 1 undefined undefined
Now, for comparison, your snippet #4 is ecactly the same:
var m = 1, 2, 3;
// Declares variables `m`, `2`, `3`, but `2` and `3` are not legal names.
The major difference between this snippet #4 and the other three is that the comma means a completely different thing here. In snippets #1 to #3, it is the Comma Operator, whereas in snippet #4, it is part of the Variable Declaration syntax.
There are other places where the comma can mean something different, for example parameter lists (function foo(a, b, c) or (a, b, c) => bla) or argument lists (foo(1, 2, 3)). In both cases, the comma is not the Comma Operator but part of the parameter list or argument list syntax to separate the parameters or arguments.
Observe:
console.log(1, 2, 3);
// 1 2 3
console.log((1, 2, 3));
// 3
In var num = (1, 2, 3); num is being set to the value of the final expression in the set. It's like a shorthand to evaluate multiple expressions and return the last value.
y = (1, 2, 3); is the same thing when you're at top level scope.
z = 1, 2, 3; is three expressions, z = 1, 2, and 3. z is set to 1, but if you logged the output of that line, you'd get 3.
var m = 1, 2, 3; is a syntax error because in this situation, the interpreter is expecting a single var pattern like var m = 1, n = 2, o = 3.

Javascript Dictionary is returning both a value and "undefined" [duplicate]

I have already read the following SO posts:
Why does this JavaScript code print “undefined” on the console?
Why does Chrome & FireFox console print undefined?
Why does the JS console return an extra undefined?
But none of it explains why the JavaScript console prints undefined when I declare a variable as follows:
var a;
It prints the result of this expression - which is undefined. And yes, var a is a valid expression on its own.
Actually, you should rather be amused by why console prints undefined when you write var a = 3 or something like this. It also prints undefined if function anyFunctionName() {} statement is processed. In fact, all the var and function declaration (!) statements seem to be ignored if there's another statement with some 'real' result:
>>> var a = 3;
undefined
>>> var a = 3; a = 4;
4
>>> var a = 3; a = 4; var a = 5; function f() {};
4 // !!!
Now, I suppose the real reason behind is behaviour of eval statement, as described here:
Let result be the result of evaluating the program prog.
If result.type is normal and its completion value is a value V, then return the value V.
If result.type is normal and its completion value is empty, then return the value undefined.
So now the question is, what does var a = 4 statement return? Guess what: it's not 4.
The production VariableStatement : var VariableDeclarationList; is
evaluated as follows:
Evaluate VariableDeclarationList.
Return (normal, empty, empty).
Now the most interesting part: what happened in the last example, why 4 is the result? That's explained in this section:
The production Program : SourceElements is evaluated as follows:
Let result be the result of evaluating SourceElements.
[...]
The production SourceElements : SourceElements *SourceElement* is evaluated as follows:
Let headResult be the result of evaluating SourceElements.
If headResult is an abrupt completion, return headResult.
Let tailResult be result of evaluating SourceElement.
If tailResult.value is empty, let V = headResult.value, otherwise let V = > tailResult.value.
Return (tailResult.type, V, tailResult.target)
Both function f() {} and var a = 5 statements' return values were (normal, empty, empty). So the script ended up with giving out the result of the first statement (starting from the script's end, so technically it's the last one) that's not (normal, empty, empty). That is the result of a = 4 assignment statement - which is 4.
P.S. And now for some icing on the cake: consider the following:
>>> function f() {}
undefined
>>> (function f() {})
function f() {}
The difference is quite subtle: the first input is treated as a Function Declaration statement, which, according to this rule...
The production SourceElement : FunctionDeclaration is evaluated as
follows:
Return (normal, empty, empty).
... will eventually produce undefined when eval-ed, as we already know.
The second input, however, is treated as a Function Expression, which is evaluated to the function itself. That means it'll be passed through eval and eventually returned to the console (in its format).
var a=1;
a
gives:
1
while
var a=1;
gives:
undefined
in the first case the console evaluates a so it prints the value of a
in the second case the console does not evaluate the value of a, but it evaluates the expression itself.
because all you are doing is declaring there is a variable - what is it? a string, an integer, a boolean - we don't know yet - hence undefined
Each time you evaluate a line of code, you get a completion type/record result which has 3 attributes: type, value and target. According to the Ecma specification:
If result.type is normal and its completion value is a value V, then return the value V.
If result.type is normal and its completion value is empty, then return the value undefined.
It turns out that when you declare a variable or a function, the completion type is (normal,empty,empty). Since the result.type is normal and value is empty, it returns the value undefined.
However when you type a = 3, it's an assignment expression and its completion type is (normal, GetValue(), empty). So you will just see 3 in the console.
For terms around statement and expression, see difference statement/expression.
For different values of completion type, see completion type documentation.
If you check the completion type documentation, you can see that empty statement ; has also a completion type (normal, empty, empty) (so it should return undefined), and indeed it's the case. For the same reason, if (x>3) {console.log(x)} also returns undefined and do {console.log(3);} while (false) too.
However, (function f(){}) doesn't return undefined because it's an expression statement.
Test by yourself. Here are some more examples:
eval('function f(){}'); // Return (normal, empty, empty), undefined
eval(';'); // Return (normal, empty, empty), undefined
eval('(function f(){})'); // (normal, GetValue(exprRef), empty), ExpresionStatement
function foo() {
return 4;
} // Return (normal, empty, empty), undefined
foo(); // (return, 4, empty), 4
eval('function foo() {return 5;}'); // Return (normal, empty, empty), undefined
eval('foo();'); // (return, 4, empty), 4
let x = 4; // (normal, empty, empty), undefined
if (x > 3) {
console.log(x);
} // (normal, empty, empty), undefined
console.log(6); // (normal, empty, empty), undefined
eval('let x = 4; if (x>3) {console.log(x)}'); // undefined
let y = 5; // (normal, empty, empty), undefined
do {
console.log(3);
y++;
} while (y < 8); // this returns y, can you explain why?
do {
console.log(3);
} while (false); // undefined since (normal, empty, empty)

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

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.

Javascript setter returns value without validation

JavaScript setter updates the internal value at the reference but the return value is not correct.
var Game =
{
get points() {
return this._points;
},
set points(x){
x = Math.min(x,25);
this._points = x;
return this._points;
}
};
Game.points = 10 ;
console.log(Game.points); // outputs 10
var updatedPoints = (Game.points = 60);
console.log(updatedPoints); // outputs 60
console.log(Game.points); // outputs 25
Expected value for 'updatedPoints' was 25 !
Any idea why this could be happening ? Can you suggest if there is a way to fix this ?
Reason to fix this: To make sure the JS code performs as expected , maintainibility !
The JavaScript simple assignement (=) returns the right value as per the specification (11.13.1). This behavior ignores whatever validation happens in your setter.
From the spec:
The production AssignmentExpression : LeftHandSideExpression =
AssignmentExpression is evaluated as follows:
Let lref be the result of evaluating LeftHandSideExpression.
Let rref be the result of evaluating AssignmentExpression.
Let rval be GetValue(rref).
Throw a SyntaxError exception if the following conditions are all true:
Type(lref) is Reference is true
IsStrictReference(lref) is true
Type(GetBase(lref)) is Environment Record
GetReferencedName(lref) is either "eval" or "arguments"
Call PutValue(lref, rval).
Return rval.
So there is no way to "fix" your issue as it is by design. Checking Game.points should be enough.

Define variables with "{}object", unexpected TypeError

If {}object, e.g. {}"string", {}[1, 2, 3], or {}({}) is exactly equal (according to ===) to object, e.g. "string", [1, 2, 3], or ({}), why can you define a variable with the latter but not the former?
To clarify:
{}"string" === "string" // true
var a = "string" // No error
var a = {}"string" // SyntaxError: Unexpected string
var a = ({}"string") // SyntaxError: Unexpected string
var a = {}("string") // TypeError: object is not a function
var a = ({}("string")) // TypeError: object is not a function
In this context, the {} seems to be behaving like an empty block, not as an object literal. So think of it syntactically like:
// Valid syntax...
// despite confusing whitespace...
for (var i=0;i<5;i++) {}"string" === "string";
However, that empty block cannot be used on the right side of an assignment like
var a = {}"string"; //SyntaxError
And here, the {}() implies that {} is being used as a function, with parameters inside ()
var a = {}("string") // TypeError: object is not a function
In Javascript, as in C++, you can have code blocks arbitrarily placed in the program. Since Javascript does not have block scope, as C++ does, this is basically useless in Javascript.
Here is an example of a non-empty code block:
{
//this is a code block
var a = 1;
var b = a + 1;
}
alert(a); // 1
alert(b); // 2
Note that these lonely code blocks are not expressions, so it is not possible to put them inside of a statement. This explains why var a = {}"string" and other uses of it inside of a statement are not syntactically valid.
{}"string" === "string" returns true in a console because {} is an empty code block, which is ignored, followed by "string" === "string", which is obviously true. If you use whitespace like this, the code is clearer:
{
//empty code block
}
"string" === "string"
Inside of an expression, {} is interpreted as an object literal. If you try something like {}() it will complain that {} is not a function.

Categories