Why does the regular assignment statement (say, x = 5) return the value assigned (5 in this case), while the assignment combined with a variable declaration (var x = 5) returns undefined?
I got the return values by executing these statements in the Chrome browser's Javascript console:
> var x = 5;
undefined
> y = 5;
5
That's the way the language was designed. It is consistent with most languages.
Having a variable declaration return anything other than undefined is meaningless, because you can't ever use the var keyword in an expression context.
Having assignment be an expression not a statement is useful when you want to set many variable to the same value at once:
x = y = z = 2;
It can also be used like this:
x = 2*(y = z); // Set y = z, and x = 2*z
However that is not the most readable code and it would probably be better written as:
y = z;
x = 2*z;
That's because var x = 5; is a variable statement, not an expression.
The behaviour of this statement is described in Section 12.2 of the ECMAScript Language Reference.
Evaluate VariableDeclarationList.
Return (normal, empty, empty).
This is basically a void return value.
The assignment operator (i.e., the equals sign) (1) assigns the right-side-operand (i.e., a value or the value of a variable, property, or function) to the left-side-operand (i.e., variable or property) and then (2) the assignment expression (e.g., y = 10) becomes a simple operand with the same value as its right-side-operand (e.g., 10) before the rest of the expression is evaluated. This is similar to when a called function is replaced with its return value when an expression is evaluated (although function calls are first in the order of operations and assignment operations are fourteenth):
var x, y, z = 1;
x = z + (y = 2); // x === 3
function returnTwo () {
return 2;
}
x = z + returnTwo(); // x === 3
Take note that not only does x now equal 3, but the entire expression evaluates to 3.
The purpose of the var keyword is to bind variables to the current scope. Variables declared with the var keyword are bound to the scope where they are declared. The var keyword assigns the left-most variable (or property) as a reference to the value of the evaluated expression:
var fun = function () {
var x = 1;
var y = x + 1;
return y;
}
// The x and y variables are bound to the scope of the fun function.
Using the var keyword with an expression is called a declaration. Declarations are actions that do not evaluate to a value, not even undefined (even though your console is printing undefined). Further, declarations cannot appear where JavaScript expects an expression, as other answers to this post have shown.
When you write var x = 5; it declares x and initalizes its value to 5.
This is a VariableStatement, it returns nothing,
but x=5 is an expression that assigns 5 to x.
as there is no x, JavaScript implicitly creates a global x in normal code
I edited my answer because of comment and some other answers.
Assignment operator doesn't return anything... In below example, first thing JS parser does is assigning 5 to y. Second thing is assigning y to x, and so on. Assigning is not return (it's not a function, and in JS it doesn't have C++ syntax to overload operator's behavior). return is sth more complex then assignment. return construct is not only returning a value, but is closing current context causing it to be destroyed. Also it's closing any parent context (closure pattern) if there is no other child using it. So please, DO NOT tell me (in comments) that assignment operator returns any value. Assignment operator in case of JS is only a language construct.
This language construct is useful in chains (and that's why everyone is talking about returning):
a = x = y = 5;
Any undeclared variable is declared automatically by parser in global scope. If you declare variable explicitly, then you can't at the same time use it in chain, like this:
a = var x = y = 5;
Proper use of above code would be:
var x = y = 5;
a = x;
Of course you can use brackets, to make your code clearer, but it doesn't mean that code behaves like a function.
Also your example works only in JS console, which is not returning but printing the result of statement, or expression. It means that JS console treats result of declaring of variable as undefined (same when creating function: function a() {}).
Related
The old style JavaScript var declaration outside of a closure is global (top-level scope) and can be accessed in a browser from the window object. For example, the declaration var x = 3; can be accessed with window['x'].
How do you similarly access a const or let declaration given the name (string) of the declaration?
var x = 3;
const y = 7;
let z = 21;
console.log('x = ' + window['x']); //x = 3
console.log('y = ' + window['y']); //y = undefined
console.log('z = ' + window['z']); //z = undefined
For the above example, how do you get the values 7 and 21 for "y" and "z" instead of undefined?
Fiddle with the code:
https://jsfiddle.net/g78ah6we/
Edits (notes added for clarity):
1. While not typical, there are use cases, such as from within a library, that it's necessary to access a declaration based only on the name of the declaration.
2. Only read access is needed (none of the declarations will be modified).
3. The window object is mentioned just to show the old way, but this question is not actually about using the window object (or the global object).
Using indirect calls to eval
Accessing global const and let definitions can be done using an indirect call to eval. That is make eval the result of a comma separated expression or assign it to a variable first. If the syntactic access is not directly to the built-in eval function it's an indirect access, and indirect access executes in global scope.
You can also set global let variables by building script to perform the setting operation.
"use strict";
let myVar = "global variable myVar";
console.log( myVar);
(function myLibrary() {
const myVar = "local variable myVar";
const indirectEval = eval;
var varName = "myVar";
console.log( eval(varName)); // direct call uses local scope
console.log( indirectEval(varName)); // indirect call uses global scope
var result = "\"updated global variable even though shadowed\"";
var js = varName + '=' + result;
indirectEval(js);
// but trying to define a new let variable doesn't attach to global scope
var js2 ='let letVar2 = "let variable two"';
indirectEval( js2);
})();
console.log( myVar)
console.log( "letVar2: " + typeof letVar2);
What you can't do is add a let or const variable to global scope using an indirect call to eval: they are block level declarations and the code eval evaluates is considered a block - so the declarations are discarded when (indirect call to ) eval returns.
PS. This is a technical answer. And yes, I have heard that "eval is evil" before, one or three times.
For read access only using hard-coded variable name strings (to prevent code insertion) you could use the pattern:
(0,eval)("identifierString");
as for example:
var x = 3;
const y = 7;
let z = 21;
{
const y = "shadow"
let z = 42;
console.log('x = ' + (0,eval)('x')); //x = 3
console.log('y = ' + (0,eval)('y')); //y = 7
console.log('z = ' + (0,eval)('z')); //z = 21
}
Indirect vs direct calls to eval
A direct call to eval only obtains the values of global variables that have not been shadowed in function scope of the call. This may restrict choice of variable names, or where the call can be made from, within the library.
An indirect call executes in global scope and can obtain the value of global variables irrespective of name shadowing within the library.
Creating a new Function object from source text, and calling it, may provide be an alternative to using an indirect call to eval in a web page. However the difference is largely semantic rather than one being better than the other.
Issues
If the global variable name (var, let, const or class identifier) comes from user input it really should be checked for validity (not all that easy) or at least accessed within a try/catch block to trap used of undeclared identifiers or use of name declarations before initialization.
Personally I would recommend finding alternatives to using global variable name strings in general. Providing a static name space object on the library (e.g. myLibrary.data) and processing string values that are property names of the object, or including option object parameters in library calls, come to mind.
Both let and const are block scoped.
In contrast, the variable declarations without var keyword creates variables in the outermost functional scope bubble. In browsers, the outermost functional scope is controlled by the window object.
What the window object doesn't control is the outermost block scope.
If your code doesn't work without being able to access variables in the window[nn] pattern, there definitely is a design issue in it.
I've marked traktor53's answer as accepted and upvoted it because it contains the technical core of the solution.
In case it's helpful for anyone, here's a solution wrapped up into a function that prevents executing code in the declaration.
var x = 3;
const y = 7;
let z = 21;
const malware = 'alert("Game over.");';
function getDeclaration(name) {
var identifierPattern = /^[_$a-zA-Z][_$a-zA-Z0-9]*$/;
var topLevelGet = (null, eval);
return identifierPattern.test(name) && topLevelGet('typeof ' + name) === 'number' ?
topLevelGet(name) : null;
}
console.log(getDeclaration('x')); //output: 3
console.log(getDeclaration('y')); //output: 7
console.log(getDeclaration('z')); //output: 21
console.log(getDeclaration('bogus')); //output: null
console.log(getDeclaration('malware')); //output: null
console.log(getDeclaration('if')); //EXCEPTION: unexpected keyword
Notes:
Be aware that the identifierPattern regex is very simplistic (does not handle all valid characters and trips up on reserved words... as traktor53 pointed out, "This is more complicated than you might think").
Change 'number' to 'object' or whatever is appropriate for your needs (for simplicity in the original question I used examples with numbers, but my real use case actually looks for objects).
Fiddle with the code:
https://jsfiddle.net/g78ah6we/6/
This question already has answers here:
What is the temporal dead zone?
(3 answers)
Closed 5 years ago.
I’m sure it’s a little caveat somewhere but, here is the issue:
var x = x || {}; // Works!
let y = y || {}; // ReferenceError: can't access lexical declaration 'y' before initialization
const z = z || {}; // ReferenceError: can't access lexical declaration 'z' before initialization
The let and const cases throw “ReferenceError: can't access lexical declaration 'variableName' before initialization”.
What is going on here? I can kind of understand the const one, but how is let not working either?
Variables declared with var are hoisted to the top of the enclosing function or global scope. They are assigned the initial value of undefined. Hence the example line of code var x = x || {}; is transformed by hoisting and default value initialization into this equivalent code:
var x = undefined;
x = x || {};
Because x already has a value when the right-hand side of the assignment statement is evaluated, the code proceeds normally.
Side note: You can declare a variable with var even after the first assignment (due to top-hoisting), and declaring more than once is idempotent (e.g. var x; var x;).
Variables declared with let and const are hoisted to the top of the enclosing block, but are not given a default value. If such a variable is read before a value is assigned, a ReferenceError is thrown. Your example line of code is transformed by hoisting into this equivalent code:
let y;
y = y || {};
To execute the assignment statement, the right side is first evaluated, then its value is assigned to the variable on the left side. But in this case, the right side reads y, which has not been assigned a value yet — hence an error is thrown.
Everything that is in the same scope as the let statement, but executed earlier (i.e. above let or the right-hand side of a let assignment) is within the Temporal Dead Zone (TDZ).
Note that in JavaScript, a piece of code like let x = x + 1; throws an exception only if execution reaches that statement. Whereas in other languages like Java, the same piece of code is always a compile-time error, even if it’s buried inside an if(false){...}. This emphasizes the behavior of variable hosting, and the fact that JavaScript examines what the code means when it is executed, hence these examples are not syntax errors in a strict sense.
Can anyone explain the hoisting behavior in the below scenarios?
alert(x);
var x = 10;
Result: alert-undefined
In the below case, x is not defined using 'var' keyword - hence attaches to the global scope.
alert(x);
x = 10; // not given a var
Result: error
In case if we replace x with window.x, we get alert as undefined!
alert(window.x); // calling the variable with window namespace
x = 10; // not given a var
Result: alert-undefined
Can you explain why it is different in calling a variable (which is attached to global scope) with the variable name (x) and with window namespace (window.x)?
The term "hoisting" is often misunderstood to mean that certain statements are moved to the top of their execution context, which is not what happens and why the term should be avoided.
What actually happens is that all declarations are processed first, so any variable declared with var (and any function created by a function declaration) exists before any code is executed. Then code execution begins.
So the variable x is created and assigned the value undefined before any code is executed (per ECMA-262), then later, during execution, it may be assigned some value.
So in the case of:
alert(x);
var x = 10;
x exists when alert is called, but has yet to be assigned a value other than undefined.
In:
alert(x);
x = 10;
x is not declared, so it does not exist when alert is called, hence the error. After the alert (if the code kept running), the assignment to x would create a property of the global (window in a browser) object named x and assign it a value of 10.
In:
alert(window.x);
x = 10;
It is a quirk of ECMAScript that global variables are also available as properties of the global object. The expression window.x attempts to read the x property of the window object. Since no such property exists, the return is undefined. Then the next statement creates x as a global variable and hence window.x now exists with a value of 10.
var x hoists the variable in the entire scope it's valid in, so the name x is available and valid anywhere within the scope. Its initial value is undefined, it only receives its value after the alert.
In case of a plain x, the variable is not hoisted because there's no var, so the bubbling up to window and its creation there only happens on the line x = 10, which is after the alert, which means the variable is entirely undefined and invalid at the time you try to alert it.
Any non-existing property of any object returns undefined, so testing window.x at a time when that property isn't set returns undefined as expected. That's how you do membership testing in Javascript: check whether a specific property equals undefined.
In the first case, var x gets hoisted, so x does exist when you call it (even though the value is undefined)
In the second case, when you say y = 10, you're essentially saying window.y = 10, so there's no hoisting at all, that's why it can't find the variable at all and gives you an error.
alert(x);
var x = 10;
will treated as
var x;
alert(x);
x = 10;
So at the time of alert x has the value undefined by default;
alert(x);
x = 10;
will be treated as
alert(x);
x = 10; // If this line runs in use strict mode then this line will throw error otherwise in non strict mode x will be added to global object ie. window
So will throw error because of x is undefined at the time of alert
alert(window.x);
x = 10;
will be treated as
alert(window.x);
x = 10;
alert will alert undefined because window is an object and it is having no property named as x at the time of alert so undefined
Later the line x = 10; will add the x to global object if in non strict mode, and will throw error if in strict mode
Is it possible to reference a JavaScript variable by a text alias? For example:
var x = 2;
var y = convertToVariableRef("x");
After calling the above function:
"y would be the same reference as x and not just simply copying the value of x into y".
if you declare an object with out any scope of function its a property of window object, so you can get it reference like this
function convertToVariableRef (ref) {
return window[ref];
}
var x = 2;
var y = convertToVariableRef("x");
but it just copy the value for primitives , and reference only for non-primitives.
array,objects etc are non-primitives.
var x = [1];
var y = convertToVariableRef("x");
y[0] = 2;
// log: x --> [2]
You could go right on and create an Object reflect and assign properties to it.
var reflect = new Object();
reflect.x = 2;
var y = reflect["x"];
Fiddle: http://jsfiddle.net/wE4Ft/
Referencing a primitive variable such as an integer is not possible. If you really want it you could have some listeners watching your value changes by adding some extra complex padding like some frameworks do (such as AngularJS).
But actually, it would be way simpler to wrap your value into an object, which is referentiable, ie:
var x = {value: 2};
var y = x;
Just use x.value or y.value then.
After calling the above function, y would be the same reference as x and not just simply copying the value of x into y.
No, JavaScript doesn't have references to variables in that sense.
You're better off using an object and a property:
var obj = {x: 2};
var yobj = obj;
consoel.log(yobj.x); // 2
yobj and obj both refer to the same object in memory in the above, and that object has an x property. So modifying x through either reference updates that one object.
Why I said "JavaScript doesn't have references to variables in that sense" above: JavaScript's closures receive an implicit reference to a hidden object, called the variable binding object, that in turn refers to variables. So in that sense, JavaScript has references to variables (indirectly, through a hidden object). But you can't get a reference to that hidden object, so it doesn't really help with what you described.
1) If the value of the variable is a primitive (number, bool, string), you cant get a reference to it. You can only copy it.
2) Even if the variable is NOT a primitive, but is attached to the current scope (i.e., declared with var, like in your example) it's impossible (with two exceptions).
The case that would work is hence a non-primitive, that's part of some other object than the current scope. Like this:
var obj = { a: [1,2,3], b: 42 };
var copyOfA = obj.a;
// do something to copyOfA
copyOfA.splice(1);
// now this has changed!
console.log(obj.a);
The two exceptions are:
Using eval (which is not very idiomatic)
If the current scope is the global scope (which it most often isn't, since you're not
making everything global, RIGHT?)
For objects and arrays, two variables can point to the same object or array, but for simple values like numbers or booleans, there is no way in javascript to have two variables pointing to the same primitive value.
You can wrap a primitive value into an object and solve the problem that way.
var x = {val: 2};
var y = x; // now both y and x point to the same object
x.val = 3;
console.log(x.val); // 3
console.log(y.val); // 3 (same value)
And, with property names, you can also use the string name to access the property as in:
var x = {val: 2};
var y = x; // now both y and x point to the same object
console.log(y["val"]); // 2
why is it that when I do this:
x = 5
(function bla(varX){
varX = 7; console.log(x); //7
})(x);
console.log(x); //5
the x doesn't change, since it's in a closure if I understand correctly,
but here:
x = {a:5}
(function bla(varX){
varX.a = 7; console.log(varX.a);
})(x)
console.log(x.a); //7
Why does x.a gets overwritten and x doesn't?
You can do the same thing with any other function:
var o = {};
function f(x) {
x.val = "foo";
}
f(o);
console.log(o.val);
The object is floating around somewhere on the heap, and both x are merely references to that object. You only need a reference to it to alter an object, no matter how you got that reference.
On the other hand, the statement x = ...; simply overwrites the local variable to refer to something else. This is also why JS does not have "pass by reference" by the traditional (and more useful) definition.
In both functions you have a global x (on line one) and a local x (function bla(x){).
In the first example, you are just changing the values of a series of variables. Changing the value of the local x doesn't touch the global x.
In your second example, you are passing a reference to the object you create to the function, not a copy of it. In the function you modify that object by assigning a new value to one of it's properties. The value of both the global and local x remains the same (a reference to the object).