about eval statement constructs and delete - javascript

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.

Related

Javascript Why can't use let inside eval()

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.

In the block scope even after initialisation with undefined var type of variable logs out unexpected value

function show() {
var x = 10;
if (true) {
var x = 20;
}
console.log(x); // 20
}
show();
But when I not initialise the 'x' manually which is inside the 'if-statement', it initialise with undefined and hoisted to the top and should log the most recent value which is undefined , as 20 is logs out in the above example. But it logs out 10.Why?
function show() {
var x = 10;
if (true) {
var x;
}
console.log(x); // 10
}
show();
From MDN - var:
Duplicate variable declarations using var will not trigger an error,
even in strict mode, and the variable will not lose its value, unless
another assignment is performed.
So, unless you re-assign any value to x, variable declared with var will keep its value.
Re-declaring x inside the if block does not create a new variable; x is created only once.
From the Ecmascript spec - 14.3.2 Variable Statement:
A var statement declares variables that are scoped to the running
execution context's VariableEnvironment. Var variables are created
when their containing Environment Record is instantiated and are
initialized to undefined when created. Within the scope of any
VariableEnvironment a common BindingIdentifier may appear in more than
one VariableDeclaration but those declarations collectively define
only one variable.
That is why x in the following statement
var x;
doesn't implicitly gets initialized with undefined; this re-declaration statement didn't re-create the variable x.
function show() {
var x = 10;
if (true) {
var x = undefined; // re-assigned
}
console.log(x);
}
show();
Note on hoisting: Unless you know this already, variables are NOT literally hoisted/moved to the top of the scope in which they are declared; variable declarations are processed before the code execution, this is why they appear to have moved to the top of the scope.
For more details, see: MDN - var hoisting
So if you are trying to relate above both example, you might confused. see both examples in different way, you will get the answer.

Why declared variables in Javascript are non-configurable in the current scope?

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

Mutating key inside JavaScript's for..in loop [duplicate]

What's the correct way to write a for-in loop in JavaScript? The browser doesn't issue a complaint about either of the two approaches I show here. First, there is this approach where the iteration variable x is explicitly declared:
for (var x in set) {
...
}
And alternatively this approach which reads more naturally but doesn't seem correct to me:
for (x in set) {
...
}
Use var, it reduces the scope of the variable otherwise the variable looks up to the nearest closure searching for a var statement. If it cannot find a var then it is global (if you are in a strict mode, using strict, global variables throw an error). This can lead to problems like the following.
function f (){
for (i=0; i<5; i++);
}
var i = 2;
f ();
alert (i); //i == 5. i should be 2
If you write var i in the for loop the alert shows 2.
JavaScript Scoping and Hoisting
The first version:
for (var x in set) {
...
}
declares a local variable called x. The second version:
for (x in set) {
...
}
does not.
If x is already a local variable (i.e. you have a var x; or var x = ...; somewhere earlier in your current scope (i.e. the current function)) then they will be equivalent. If x is not already a local variable, then using the second will implicitly declare a global variable x. Consider this code:
var obj1 = {hey: 10, there: 15};
var obj2 = {heli: 99, copter: 10};
function loop1() {
for (x in obj1) alert(x);
}
function loop2() {
for (x in obj2) {
loop1();
alert(x);
}
}
loop2();
you might expect this to alert hey, there, heli, hey, there, copter, but since the x is one and the same it will alert hey, there, there, hey, there, there. You don't want that! Use var x in your for loops.
To top it all off: if the for loop is in the global scope (i.e. not in a function), then the local scope (the scope x is declared in if you use var x) is the same as the global scope (the scope x is implicitly declared in if you use x without a var), so the two versions will be identical.
You really should declare local variables with var, always.
You also should not use "for ... in" loops unless you're absolutely sure that that's what you want to do. For iterating through real arrays (which is pretty common), you should always use a loop with a numeric index:
for (var i = 0; i < array.length; ++i) {
var element = array[i];
// ...
}
Iterating through a plain array with "for ... in" can have unexpected consequences, because your loop may pick up attributes of the array besides the numerically indexed ones.
edit — here in 2015 it's also fine to use .forEach() to iterate through an array:
array.forEach(function(arrayElement, index, array) {
// first parameter is an element of the array
// second parameter is the index of the element in the array
// third parameter is the array itself
...
});
The .forEach() method is present on the Array prototype from IE9 forward.
Actually, if you dislike declaration within for heading, you can do:
var x;
for (x in set) {
...
}
As mentioned in other answers to this question, not using var at all produces unnecessary side-effects like assigning a global property.
Use the one where you declare the loop variable with var. Implicitly declared variables have a different scope that's probably not what you intended.
for(var i = 0; ...)
is a commonly seen pattern but it's different from
for(int i; ...)
in C++ in that that the variable isn't scoped to the for block. In fact, the var gets hoisted to the top of the enclosing scope (function) so a local i will be effectively available both before the for loop (after the beginning of the current scope/function) and after it.
In other words, doing:
(function(){ //beginning of your current scope;
//...
for(var i in obj) { ... };
})();
is the same as:
(function(){ //beginning of your current scope;
var i;
//...
for(i in obj) { ... };
})();
ES6 has the let keyword (instead of var) to limit the scope to the for block.
Of course, you SHOULD be using local variables (ones declared with either var or let or const (in ES6)) rather than implicit globals.
for(i=0; ...) or for(i in ...) will fail if you use "use strict"; (as you should) and i isn't declared.
Using var is the cleanest way, but both work as described here: https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in
Basically, by using var you ensure that you create a new variable. Otherwise you might accidentally use a previously defined variable.
I think var is good for performance reasons.
Javascript won't look through the whole global scope to see if x already exists somewhere else.
From a general point of view, first version will be for an index that must live within loop's scope, while the other one would be any variable in the scope where loop's constructor got invoked.
If you're going to use loop's index inside for loop and this won't be required by others in next lines, better declare the variable with "var" so you'll be sure "x" is for loop's index initialized with 0, while the other one, if other "x" variable is available in this context, this will get overwritten by loop's index - that's you'll have some logical errors -.
I always use the block scoped let introduced in ES2015.
for (let x in set) {
...
}
Additional reading and examples

Scope chain first looks to __parent__ or to __proto__?

Here I found information hot to works scope chain:
...before we go to the parent link, first proto chain is
considered.
Notice, that not in all implementations the global object inherits
from the Object.prototype. The behavior described on the figure (with
referencing “non-defined” variable x from the global context) may be
tested e.g. in SpiderMonkey.
I use Firefox browser for testing, but, when I set global variable x, and set to Object.prototype property x and do a() I've got 4. Why, if first we go to proto?
var x = 1;
Object.prototype.x = 2;
var a = function () {
y = 3;
return this.y + this.x;
};
a.x; // 2
a(); // 4
You should've cited the beginning of that paragraph, not the end:
At code execution, a scope chain may be augmented using with statement and catch clause objects.
So what your sentence refers to are only object environment records - scope objects in which variables live. Those are typically imaginary, described for specification purposes only (and can only be observed in Rhino), but the with statement makes an exception and prepends an actual JS object to the scope chain.
If you check out the example, you will find the with statement right there. The gist:
Object.prototype.x = "inherited object property";
var x = "parent scope variable";
with ({}) {
alert(x); // you bet what it yields
}
When I set global variable x, and set Object.prototype.x property, why doesn't it get the prototype value?
By using this in a function that is called plainly (a()), you are referring to the global object. It is indeed the object for the object environment record of the global scope, which means that all global variables live there as properties.
Now why does accessing x on that object (regardless whether by variable x or property this.x) bring up 1 not 2? Because the global variable with the value 1 is an own property of the object. The prototype chain will only be traversed if the property is not already found!

Categories

Resources