I would like someone to please tell me why Javascript does not complain when I do this:
eval("x = 1");
console.log(x);
output: 1
...However, it complains if I do this:
eval("let x=1");
console.log(x);
output:
> ReferenceError: x is not defined
Thank you.
Note: I know using eval is bad, serious security risks, blah,blah, thank you for that. I just want to understand the theory behind this.
Edit: Now that you've updated your question, I can help a little more specifically.
The reason
eval("let x = 1");
console.log(x);
doesn't work is because let is local to its scope. You cannot access the variable outside of the scope of eval.
You would need to use var to make the variable globally accessible.
To quote MDN:
let allows you to declare variables that are limited to the scope of a block statement, or expression on which it is used, unlike the var keyword, which declares a variable globally, or locally to an entire function regardless of block scope.
The reason your original, unedited question didn't work was because you were assigning the variable without actually doing anything with it.
eval("var x = 1"); // Undefined
eval("const x = 1"); // Undefined
eval("let x = 1"); // Undefined
Now if you give it something to spit back out:
eval("var x = 1; x"); // 1
eval("const x = 1; x"); // 1
eval("let x = 1; x"); // 1
At least that's the way it works in my chromium console.
The way I see it is that in the first example you're simply evaluating the response of the statement x = 1.
However, it doesn't return a response when evaluating because there is no response to return.
It's just an assignment. You are telling the eval that there is this variable called x and you are telling it that it has a value of 1 but you are not telling it what to do with that variable.
It is the equivalent of you creating the following:
doNothing.js
let x = 1;
If you run this program, it will not output anything to the console because all it is doing is assigning a value to x.
Related
I have created a variable x with the keyword var but when I do the following:
var x = 10;
delete x;
It returns false. basically, I don't want to delete the x variable but my question is that why javascript does not allow to configure the variables declared in the current scope context. This is also mentioned in this documentation, but the question is why?
Because otherwise every x might or might not throw an error or might suddenly refer to another variable:
let x = 2;
{
let x = 3;
if(Math.random() > 0.5) delete x;
console.log(x); // ?!
}
That makes code conpletely error prone and unpredictable and makes it impossible to optimize, every line might suddenly become a syntax error, and thats why it is not possible to delete variables that way.
However there is another way to get this behaviour by adding an object as scope which you can mutate, and thats the reason why no one uses the with statement:
const scope = { b: 2 };
with(scope) {
console.log(b); // 2
delete scope.b;
console.log(b); // reference error
}
You cannot delete a variable if you declared it (with var x;) at the time of first use.
However, if your variable x first appeared in the script without a declaration,
you can delete the variable if you didn't use var keyword.
you can get more information from this resource
http://www.javascripter.net/faq/deleteavariable.htm
This question already has answers here:
Why does Chrome debugger think closed local variable is undefined?
(7 answers)
Closed 4 years ago.
Code:
function test4() {
var x = 10;
var y = 100;
// inner referred x only
function inner () {
console.log(x);
debugger;
}
// inner2 referred y to make sure y is in the scope of inner
function inner2 () {
console.log(y);
}
return inner;
}
var foo = test4();
foo();
y is in the scope of inner even only inner2 which never been used refer to it. I checked the result in scope and x, y are there:
But when I checked variables in watch panel and console, I can't get all of them:
It's weird that y is in the scope but get not defined when using debugger.
So, is it means that debugger can not access variable that not used in current context even it's in the closure or it just a bug? (My chrome version is 51.0.2704.103 m)
It's similar to Why does Chrome debugger think closed local variable is undefined? but not the same. Because inner2 in my code make sure that y is in the closure. And actually my question is opposite to Louis's answer under that question.
You are a first-hand observer of the internal mechanics of scope-optimization. Scope optimization is checking to see which variables are used in the current scope and optimizing out access to unused variables. The reason for this is because in the machine code generated from JIT compilation of javascript, the whole concept of variable naming is lost. But, to maintain javascript compliance, the JIT compiler associates an array of used local variables to each javascript function. Observe the following code.
(function(){
"use strict";
var myVariable = NaN; // |Ref1|
var scopedOne = (function(){
var myVariable = 101; // |Ref2|
return x => x * myVariable;
})();
var scopedTwo = (function(){
var myVariable = -7; // |Ref3|
return x => x / myVariable;
})();
console.log("scopedOne(2): ", scopedOne(2));
console.log("scopedTwo(56): ", scopedTwo(56))
})();
As seen above, Javascript is a scoped stack-based language. If Javascript was not a scoped language, then the variables used in the functions would depend on the values of the variables at the location where the function was being executed. For instance, without a scope, scopedOne would use the value of myVariable at |Ref1| (NaN) instead of at |Ref2| (101) and would log NaN to the console. Back to the main point, in the machine code, when the debugger comes in, it can only figure out where the actual locations in memory are of the used variables since only those memory locations have persisted to machine code as only those variable have been used. The memory locations of the rest of the variables remain a mystery to it. As you have observed, this has the secondary side-effect of making unused variables in the scope "invisible" to that function. However, there is a solution.
To circumvent this problem, simply wrap the debugger; statement in an eval to force the browser to do an expensive variable lookup of all the variables in the scope. Basically, the browser has to go back to the original source code, examine it for the original names of the variables in the scope, and figure out where the values of the variables are stored by the JIT-generated machine code. Open up developer tools and run the snippet below. Then go to the prior level in the "Call Stack" panel and observe how the visibility of the value of the variable y changes from visible inside eval to invisible outside eval.
function test4() {
var x = 10;
var y = 100;
// inner referred x only
function inner () {
console.log(x);
eval("debugger;");
}
// inner2 referred y to make sure y is in the scope of inner
function inner2 () {
console.log(y);
}
return inner;
}
var foo = test4();
foo();
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.
I am learning javascript and my question might not be used practically anywhere but for interview purpose I want to understand this clearly. below is the code. the first alert alerts 'undefined' and the second one is '4'. the second one is understandable. I want to know why the first alert doesn't alert '5' and undefined? whats the concept behind the same?
Thanks.
var x = 5;
function check(){
alert(x);
var x = 4;
alert(x);
}
check();
var is always hoisted (moved) to the begining of the scope. Your code is equivalent to this:
var x = 5;
function check(){
var x;
alert(x);
x = 4;
alert(x);
}
check();
As you can clearly see, the local x hides the global x, even if the local one doesn't have a value yet (so the first alert shows undefined).
A little extra: conditionals or other blocks don't influence hoisting the var declaration.
var x = 1;
(function() {
x = 3;
if (false) {
var x = 2; // var will be moved to the begining of the function
}
})()
console.log(x) // 1
JavaScript has some concepts that take a little getting used to for sure and sometime the answer for what seems to be a simple question is long winded but i will try to be as quick and concise as possible.
In JavaScript, variables are scoped by functions and you have two places that a variable can be scoped(have context) either 'globally' or 'locally'. The global context in a web browser is the 'window' so in your code example var x = 5; is a global variable. The local context in your code is within your check function so var x = 4; is a local variable to the check function. If you had more functions those would have their own function scope(context) and so on. As a word of caution if you forget the word "var" when declaring a variable inside a function it will become a 'global' variable, be careful. Now that we have the 'global', 'local' scope down what happens before a Javascript programs runs?
A few things happen in the Javascript Engine before your code is executed but we will only look at what happens to variables. Variables are said to be 'hoisted' to the top of their functional context or scope. What that means is that the variable declaration is 'hoisted or moved to the top and assigned the value 'undefined' but not initialized the initialization remains in the same spot they were. This hoisting also makes the variable available anywhere within the function it was defined.
So it looks something like this:
//to be accurate this is hoisted to its functional scope as well
var x;
x = 5;
function check() {
//declaration of 'x' hoisted to the top of functional scope,
//and assigned undefined
var x;
//this will alert undefined
alert(x);
//initialization of variable remains here
x = 4;
//so after the initialization of 'x' this will alert 4
alert(x);
}
check();
Happy Coding!!!
As an aside: They have a new feature named 'let' that you can implement to add block-level scoping in I believe EMCAScript 6 but is not implemented in all the browsers as of yet so that is why i placed it in an aside.
If I run the following code in google chrome console I get the following results
var x = 1;
alert(delete x); // false
eval('var y = 2');
alert(delete y); // true
Why in the first example the variable is not deleted and in the second example it is deleted?
From the Mozilla JS Docs for delete:
delete is only effective on an object's properties. It has no effect
on variable or function names.
The example provided is similar to yours.
x = 42; // creates the property x on the global object
var y = 43; // declares a new variable, y
delete x; // returns true (x is a property of the global object and can be deleted)
delete y; // returns false (delete doesn't affect variable names)
So, why does alert(delete y); work? I couldn't nail down the exact answer, but basically you cannot rely on the scope of eval.
I think eval('var y = 2'); does not get declared as a variable and is treated as a property, but I haven't found evidence for that yet other than the results of our tests. I'm going to continue researching to see if I find the exact reason.
Other articles on eval weirdness:
http://wingolog.org/archives/2012/01/12/javascript-eval-considered-crazy
http://brownplt.github.com/2012/10/21/js-eval.html
http://blog.rakeshpai.me/2008/10/understanding-eval-scope-spoiler-its.html
EDIT 0
Based on #Xavier Holt's comment, I looked up hoisting and scope in regards to eval. This scope cheatsheet from Mozilla Dev docs had the following:
eval may capture assignments, but not var declarations
eval'd vars hoist normally, so evals may capture assignments similar
to with:
function f() { {
let x = "inner";
eval("var x = 'outer'");
print(x); // "outer" }
}
If I'm reading this correctly, then my earlier assumption was right. eval() does not evaluate var declarations by declaring a variable. It must create a property or be treated as a property in order for delete to work.