Here is the issue:
var x = 5;
window.x === x // true. x, as it seems, is a property of window
delete x; // false
delete window.x; // false;
BUT
window.x = 5;
delete window.x; // true
AND
window.x = 5;
delete x; // true
What is the explanation for such behavior?
Essentially the reason is that declared variables are created with an internal DontDelete attribute, while properties created via assignment are not.
Here is great article explaining the inner details of delete: Understanding delete
When declared variables and functions become properties of a Variable
object — either Activation object (for Function code), or Global
object (for Global code), these properties are created with DontDelete
attribute. However, any explicit (or implicit) property assignment
creates property without DontDelete attribute. And this is essentialy
why we can delete some properties, but not others:
You can use delete only for deleting objects, object properties or array element.
delete expression
delete will be not working if expression can't be represented
as property. So delete can remove global variable, but not variables inited by var.
So, let me explain:
var x = 5;
You create variable in global scope by var, not property of window object. This var is just linked to window.x. And then you compare window.x === x it will return true. But:
delete x; // deleting variable, not property, return false
delete window.x; // resolve link to x and also deleting variable, not property, return false
BUT
window.x = 5;//add property
delete window.x; // can delete property, return true
AND
window.x = 5;//add property
delete x; //resolve x. it's a propery of window, return true
and older
In ECMAScript 262/3 as #Peter explain is available DontDelete flag. But in ECMAScript 262/5.1 in strict mode deleting is regulated by Configurable flag:
When a delete operator occurs within strict mode code, a SyntaxError
exception is thrown if its UnaryExpression is a direct reference to a
variable, function argument, or function name. In addition, if a
delete operator occurs within strict mode code and the property to be
deleted has the attribute { [[Configurable]]: false }, a TypeError
exception is thrown.
This is how I understand that:
var x = 5; declared in the global scope creates the new window property x.
window.x = 5; declared (whereever) creates the new window property x as well. That's why window.x === x gives you true.
The difference is that javascript by default sets different descriptors for x property according to the way (one of two above) it is declared.
var x = 5 is equal to:
Object.defineProperty(window,'x',{
value: 5,
writable: true,
enumerable: true,
configurable: false
});
while window.x = 5 is equal to:
Object.defineProperty(window,'x',{
value: 5,
writable: true,
enumerable: true,
configurable: true
});
The configurable descriptor, if false, forbides to delete the property.
We can assume, that javascript use Object.defineProperty with different descriptor settings under the hood when we declare variables in a simple way with var keyword or without it (automatically assigned to window).
You can simply check that:
var x = 5;
window.y = 5;
console.log(Object.getOwnPropertyDescriptor(window,'x')); //configurable:false
console.log(Object.getOwnPropertyDescriptor(window,'y')); //configurable:true
Related
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!
I found this code:
if (!("aa" in window)) {
alert('oh my god');
var aa = 1;
}
alert("aa" in window);
alert(aa);
This code the second alert is alert true,but,the third alert is 'undefined',and the alert in the 'if' is not run. Why?
I think the reason is the in; what is its effect?
I searched on Google, but found nothing, because Google thinks the word ‘in&srquo; is a filter word.
We always use the in in loops, but, frankly speaking, I use it but don’t really understand it.
This tests if the window object has a property (filled or not) whose key is "aa".
This operator is very useful because it works even if the value is undefined :
window.aa = undefined; // or just aa=undefined if you're in the global scope
console.log('aa' in window); // logs true
It also works if the property isn't enumerable :
console.log('length' in []); // logs true
In your case, there may not be an aa value, but if the alert shows you true, the property was added to window.
MDN reference on in
Note that the for...in statement is different in that it doesn't really use the in operator but is a specific construct.
MDN reference on for...in
EDIT : an explanation of your edited question (very different from the first one) :
Your confusion seems to arise from the fact you declared the var aa = 1; in a block. You should know that the scope of a variable in JavaScript is either a function of the global scope and that declarations are hoisted. So your code is in fact equivalent to
var aa = undefined;
if (!("aa" in window)) { // aa is in window, so we don't enter here
alert('oh my god');
aa = 1;
}
alert("aa" in window); // yes, the property exists, it's true
alert(aa); // aa is still undefined
Taking the alerts in order:
alert #1 is never reached because ("aa" in window) === true so the if boolean condition is false.
JavaScript has function scope and the variable aa is "hoisted" up to the top of the scope first, so it is defined.
alert #2
"aa" in window is true because the variable was added to the window object when it was hoisted up. Equivalent to just writing:
var foo;
"foo" in window (which === true)
alert #3
From the Standard ECMA-262 ECMAScript Language Specification :
A variable statement declares variables that are created as defined in 10.5. Variables are initialised to undefined when created. A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.
So aa is undefined since the assignment was never executed.
in checks if property exists in Object
// in the below snippet they are checking if 'aa' property exists in 'window' Object . Since variables are function declarations are hoisted. you'll never enter inside if block .
var aa = undefined ;
if (!("aa" in window)) { //2 => 'aa' property exists
alert('oh my god');
aa = 1; // 1 => hoisted
}
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.
I was reading this article on the Javascript delete method, and tried this code:
function f(){
var x = "abcd";
console.log(delete x); //returns false, because x has DontDelete attribute
y = "abcd";
console.log(delete y); //returns true, but I didn't explicitly assign y as a property
}
This is strange, because I am not using property assignment for y (as in: window.y="abcd";), which the article claims is how you make properties deleteable.
Why does y not have the DontDelete attribute?
UPDATE:
In the answers below, it is claimed that y is deleteable merely because it is a property of the window object. That is not true. Consider the following snippet:
function f() {
var functionx = "abcd";
console.log(delete functionx); //returns false, because x has DontDelete attribute
functiony = "abcd";
console.log(delete functiony); //returns true, but I didn't explicitly assign functiony as a property to the window
}
//take 1
var globalx = "abcd";
console.log(this.globalx);//to prove x is a property of the global
console.log(delete globalx);//fails, even though x is a property of the window
//take 2
this.globaly = "abcd";
console.log(this.globaly);//to prove y is a property of the global
console.log(delete globaly);//succeeds, because I used property assignment
f();
In take 1, globalx is a property of the window, but it is still undeleteable. The criteria for whether delete will succeed is not whether the variable is a property of something (which is always true), but whether that property has a DontDelete attribute set.
delete x is invalid because it does not resolve to any object property.
y resolves to a property of the window object.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/delete:
delete expression
where expression should evaluate to a property reference
Here is the expanded version of the second example:
window.y = "abcd";
console.log(delete window.y);
It's pretty obvious now what's happening.
If you don't use the var keyword, the interpreter assumes the variable is a property of the global object, (which is usually window), as shown in this JSFiddle. Note that in the global scope, this is the same as the window object.
Re: Update:
Outside a function, a variable declared with var is defined as a non-configurable property of the global object. This is detailed in https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/var, where it says:
Using var outside a function is optional; assigning a value to an undeclared variable implicitly declares it as a global variable (also a property of the global object). The difference is that a declared variable is a non-configurable property of the global object while an undeclared is configurable.
Declaring a variable inside a function does not assign it to a global property, but rather a local variable.
That explains the behaviour you're seeing when defining a variable in the global scope.
Variables in JavaScript are declared using var keyword. When you omit var, the interpreter starts looking for the closest scope in which the given variable is defined. If the given variable is not found, it will be created as a new property on a global object.
This is exactly what happens here:
Variable y is referenced and assigned a new value (abcd)
Var y is not defined anywhere → new global property y is created and assigned the value (effectively window.y = "abcd").
As a result, y becomes property of the global object, which can be deleted.
Please consider the following snippet (fiddle here):
var a;
a = 1;
console.log(delete a); // prints 'false'
b = 1;
console.log(delete b); // prints 'true'
Why does the delete keywords behave differently on the global variables a and b?
From the MDN docs:
The delete operator removes a property from an object.
A global variable (without var) is a property on the global object (typically window), so can be deleted.
A var is not a global variable, but a local variable in the outer scope - not a property of the global object - so delete does not delete it. From those docs:
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)
MDN says delete returns false only if the property exists and cannot be deleted. It returns true in all other cases. After deleting, try testing the actual values. You'll see that a was not deleted. This is because, as the MDN page says, delete will not affect variable names.
It has no effect on variable or function names.
(i.e., defined with var and not off the global object)
Take a look at the examples on the following page.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/delete
From mozilla docs about var :
The difference is that a declared variable is a non-configurable property of the global object while an undeclared is configurable.
var a; --> This is a declared variable, because you are using var, so it's not configurable.
a = 6; --> This is an undeclared variable, because you are not using var, so it's configurable.
both syntaxes above will end up with a var named a attached as a property of the global object (window typically) and properties has these attributes:
Writable. If false, the value of the property can not be changed.
Configurable. If false, any attempts to delete the property or change its attributes (Writable, Configurable, or Enumerable) will fail.
Enumerable. If true, the property will be iterated over when a user does for (var prop in obj){} (or similar).
that is extracted from ecmascript5 objects and properties , and as you can read, the configurable attribute of the variable in question affects whether the variable can or cannot be deleted.
"var a" means it cannot be accessed from anywhere outside of the current block, thus deleting it WOULD mean UNDECLARE (not same as undefined), thus allowing to write "var a" again in the same block (error).
Allowed usages (MDN):
delete object.property
delete object['property']
delete object[index]
delete property
It's like GOTO and unstructured programming, where you might need to manually clean up resources, it's kind of ~Destructor in C (though not the same). You can delete an object like ~a(); but you cannot "'UNDECLARE' a variable" like "int i".