I was planning on using the delete operator in JavaScript for something, when I decided to remind myself how it worked by playing with it some. I understand that "delete" is supposed to be used on object properties, but I wanted to see how it behaves on variables. However, some weird results occurred when I put the following snippet of code in different browsers:
var one = 1;
delete one;
console.log(one); // prints out 1 in Chrome, but not in Safari or Firefox
In Safari, the JavaScript console printed out the error "ReferenceError: Can't find variable: one". Firefox gave a similar response: "ReferenceError: one is not defined". However, Chrome printed out the value of the variable, 1. Can anyone explain why Chrome behaves differently than Safari and Firefox in this regard?
Don't trust the console. Some codes behave different there.
The following will alert 1 if you run it on a real page:
var one = 1;
delete one;
alert(one);
Demo
(Tested on Firefox, Chrome, IE, Safari and Opera.)
Understanding delete explains why the console (or firebug) shows a different behavior:
Every execution context has a so-called Variable Object associated
with it. Similarly to execution context, Variable object is an
abstract entity, a mechanism to describe variable instantiation. Now,
the interesing part is that variables and functions declared in a
source text are actually added as properties of this Variable object.
Finally, variables declared within Eval code are created as properties
of calling context’s Variable object.
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.
So what happens in Firebug? Why is it that variables declared in
console can be deleted, contrary to what we have just learned? Well,
as I said before, Eval code has a special behavior when it comes to
variable declaration. Variables declared within Eval code are actually
created as properties without DontDelete.
Let's checkout the docs on delete from MDN.
The delete operator removes a property from an object.
You are using it a way that doesn't make much sense, that is to remove a local variable.
Those docs also say:
Throws in strict mode if the property is an own non-configurable property (returns false in non-strict). Returns true in all other cases.
Which means your usage here would throw an exception in strict mode, since you are using delete in an unsupported way. This is proven when you do:
var one = 1;
delete one; // returns false
And as the docs mention, a return value of false means the delete operation did not succeed.
If you use it properly, it should behave like you expect:
var obj = {one: 1};
delete obj.one; // returns true
alert(obj.one); // alerts "undefined"
I think the answer he's looking for, and someone essentially said earlier, but muddied up the clarity by adding more explanation through an edit should be restated thus:
When you use a method in a way that was not original dictated in the standard, then the result is undefined and therefore varies from browser to browser (because each essentially has a different js engine) in the way that it's handled.
Revised Answer:
Based on feedback from #Oriol concerning property attributes. I have found that the real issue here is concerning Property Attributes (See ECMA-262 edition 5.1 section 8.6.1) and the Variable Environment's execution context (See ECMA-262 edition 5.1 section 10.3)
Can anyone explain why Chrome behaves differently than Safari and
Firefox in this regard?
var one = 1;
delete one;
console.log(one); // Returns 1.. but why?
Two things are happening here:
The var declaration binds the declared object into an "Execution context" that is distinctly different from the Global (window) one.
JavaScript evaluates the [[Configurable]] property to determine if its "OK" to delete
Concerning #1
The var declaration in the code establishes a VariableEnvironment whereby the value is bound to the object in a distinctly different execution context (scope) than the global one. So naturally, when var is not used the VariableEnvironment is interpreted in a global execution binding process, making statements like one = 1; or delete one; possible.
var one = 1; // Execution context #1 has a unique VariableEnvironment
delete one; // Execution context #2 has a global VariableEnvironment
console.log(one); // Return the value from 'var one'
This complies with the language spec:
10.4 Establishing an Execution Context
Evaluation of global code or code using the eval function (15.1.2.1)
establishes and enters a new execution context...
10.4.2 Entering Eval Code
The following steps are performed when control enters the execution
context for eval code:
If there is no calling context or if the eval code is not being
evaluated by a direct call (15.1.2.1.1) to the eval function then,
Initialise the execution context as if it was a global execution
context using the eval code...
Concerning #2
Chrome and JSFiddle are both doing the right thing here. The the reason has to do with the [[Configurable]] property attribute, which is assigned to both native and user-created properties behind the scenes. When a user-created property is established, this attribute is set to true. This allows the developer to execute assign and delete commands on the property.
var test = {};
test.me = "OK" // [[Configurable]] is true so No Problem!
delete test.me // Good here too!
To prevent certain situations where object properties should not ever be deleted or modified [[Configurable]] is set to false by default. Which respects the language spec:
If the value of an attribute is not explicitly specified by this
specification for a named property, the default value defined in Table
7 is used...
[[Configurable]] false
var test2 = [1,2,3];
console.log(test2.length); // length property is '3'
console.log(delete test2.length); // NOPE [[Configurable]] is false
Same is true in function arguments in a function scope:
(function foo(one) {
console.log(delete one);
})(); // NOPE (false)
What can we draw from both findings?
From this we can understand that Firefox and Safari do not does not play by the rules. When var one=1; is declared in either of these browser's consoles, properties in this scope are incorrectly deemed [[Configurable]] by default and thus deletes var one and not the implied window.one.
In Firefox/Safari:
var one = 1; // var 'one'?
delete one; // NUKE var 'one'!
console.log(one); // ReferenceError: 'one' is not defined :'(
"OK Wait! So why then is delete one true by itself?
It plays out as determined by the language spec (10.4.2):
var one = 1; // VariableEnvironment not global or [[Configurable]]
delete one; // FALSE
...
delete one; // TRUE VariableEnvironment global and [[Configurable]]
...
var one = 1; // VariableEnvironment not global or [[Configurable]]
delete this.one; // TRUE VariableEnvironment is global and [[Configurable]]
Related
If I open a javascript console on a web page and add a variable:
var abc = "Hello";
Javascript attaches that property to the window object so I can simply access the object by window.abc or directly abc
console.log(window.abc)
console.log(abc)
Now, if I try to access something not defined yet through the window object
console.log(window.notdefinedyet)
> undefined
It simply returns undefined. So if I do
console.log(notdefinedyet)
why is that an error and not just undefined?
Because trying to read the value of an undeclared identifier is a ReferenceError. This is fundamentally a different operation from trying to read a property from an object that the object doesn't have.
Trying to use an undeclared identifier is usually a bug (for instance, a typo). That's part of why ES5's strict mode made assigning to an undeclared identifier a ReferenceError (instead of the old behavior — still in loose mode for backward-compatibility — where it creates a new global variable, something I call The Horror of Implicit Globals).
One could argue (and many have) that trying to get a property from an object that the object doesn't have is also a bug. But it's often really useful to get undefined instead of an error in that case. Whether one agrees with that distiction, this is how JavaScript is defined.
It's probably worth noting tangentially that this business of global variable declarations creating properties on the global object is legacy behavior that has to remain in place for backward compatibility, but isn't consistent with current thought on language design in TC39 (the committee behind JavaScript standards). Newer constructs like let, const, and class don't create properties on the global object even when used at global scope (although they do create globals):
var a = 1;
let b = 2;
console.log("a" in window); // true
console.log("b" in window); // false
console.log(a); // 1
console.log(b); // 2
It always check for reference so console.log(notdefinedyet) gives an error but in case of console.log(window.notdefinedyet), window has reference of object type built in only thing it does not have notdefinedyet key
Have you ever taken a look under the hood at the jQuery 1.4 source code and noticed how it's encapsulated in the following way:
(function( window, undefined ) {
//All the JQuery code here
...
})(window);
I've read an article on JavaScript Namespacing and another one called "An Important Pair of Parens," so I know some about what's going on here.
But I've never seen this particular syntax before. What is that undefined doing there? And why does window need to be passed and then appear at the end again?
The undefined is a normal variable and can be changed simply with undefined = "new value";. So jQuery creates a local "undefined" variable that is REALLY undefined.
The window variable is made local for performance reasons. Because when JavaScript looks up a variable, it first goes through the local variables until it finds the variable name. When it's not found, JavaScript goes through the next scope etc. until it filters through the global variables. So if the window variable is made local, JavaScript can look it up quicker.
Further information: Speed Up Your JavaScript - Nicholas C. Zakas
Undefined
By declaring undefined as an argument but never passing a value to it ensures that it is always undefined, as it is simply a variable in the global scope that can be overwritten. This makes a === undefined a safe alternative to typeof a == 'undefined', which saves a few characters. It also makes the code more minifier-friendly, as undefined can be shortened to u for example, saving a few more characters.
Window
Passing window as an argument keeps a copy in the local scope, which affects performance: http://jsperf.com/short-scope. All accesses to window will now have to travel one level less up the scope chain. As with undefined, a local copy again allows for more aggressive minification.
Sidenote:
Though this may not have been the intention of the jQuery developers, passing in window allows the library to be more easily integrated in server-side Javascript environments, for example node.js - where there is no global window object. In such a situation, only one line needs to be changed to replace the window object with another one. In the case of jQuery, a mock window object can be created and passed in for the purpose of HTML scraping (a library such as jsdom can do this).
Others have explained undefined. undefined is like a global variable that can be redefined to any value. This technique is to prevent all undefined checks from breaking if someone wrote say, undefined = 10 somewhere. An argument that is never passed is guaranteed to be real undefined irrespective of the value of the variable undefined.
The reason to pass window can be illustrated with the following example.
(function() {
console.log(window);
...
...
...
var window = 10;
})();
What does the console log? The value of window object right? Wrong! 10? Wrong! It logs undefined. Javascript interpreter (or JIT compiler) rewrites it this way -
(function() {
var window; //and every other var in this function
console.log(window);
...
...
...
window = 10;
})();
However, if you get the window variable as an argument, there is no var and hence no surprises.
I don't know if jQuery is doing it, but if you are redefining window local variable anywhere in your function for whatever reason, it is a good idea to borrow it from global scope.
window is passed in like that just in case someone decides to redefine the window object in IE, I assume the same for undefined, in case it's re-assigned in some way later.
The top window in that script is just naming the argument "window", an argument that's more local that the global window reference and it what the code inside this closure will use. The window at the end is actually specifying what to pass for the first argument, in this case the current meaning of window...the hope is you haven't screwed up window before that happens.
This may be easier to think of by showing the most typical case used in jQuery, plugin .noConflict() handling, so for the majority of code you can still use $, even if it means something other than jQuery outside this scope:
(function($) {
//inside here, $ == jQuery, it was passed as the first argument
})(jQuery);
Tested with 1000000 iterations. This kind of localization had no effect in performance. Not even a single millisecond in 1000000 iterations. This is simply useless.
Checking a website I have come accross with this code;
if (!window.homePosition) window.homePosition = $('#sticky-container').offset().top;
is there any difference in declaring the global variable in this two ways?
window.homePosition = 400;
or
var homePosition = 400;
Why do you think the previous developer used this notation?
Yes, there are two differences, though in practical terms they're not usually big ones.
Your have 3 statements
var a=0;
...creates a variable on the variable object for the global execution context, which is the global object, which on browsers is aliased as window (and is a DOM window object rather than just a generic object as it would be on non-browser implementations). The symbol window is, itself, actually a property of the global (window) object that it uses to point to itself.
The upshot of all that is: It creates a property on window that you cannot delete. It's also defined before the first line of code runs (see "When var happens" below).
Note that on IE8 and earlier, the property created on window is not enumerable (doesn't show up in for..in statements). In IE9, Chrome, Firefox, and Opera, it's enumerable.
a=0;
...creates a property on the window object implicitly. As it's a normal property, you can delete it. I'd recommend not doing this, it can be unclear to anyone reading your code later.
And interestingly, again on IE8 and earlier, the property created not enumerable (doesn't show up in for..in statements). That's odd, particularly given the below.
window.a=0;
...creates a property on the window object explicitly. As it's a normal property, you can delete it.
This property is enumerable, on IE8 and earlier, and on every other browser I've tried.
From code that's inside a function, the only way to create a global variable is to create a property on the global object (which, in a browser, is window).
Lots of code, especially libraries but in general well-written code, will be encapsulated in a function for the explicit purpose of avoiding the creation of global symbols:
(function() {
"use strict";
// lots of code here
})();
From inside such code, if it's desired that a global symbol be exported, the way to do that is to create a property of the global context:
(function() {
"use strict";
// lots of code here
window.something = valueToExport;
})();
Typically such encapsulating functions are written such that a local copy of a reference to the global context is available:
(function(global) {
"use strict";
// lots of code here
global.something = valueToExport;
})(this);
That works because in the actual global context — where that encapsulating function runs — the value of this is a reference to that context (window in a browser). In the above example, then, the parameter global will be a local reference to the global context object.
edit — without "strict" mode, a reference to an otherwise undeclared symbol on the left side of an assignment will, it's true, create a global symbol. The question on this page is, "How to declare global variables in JavaScript the right way." Using implicit global property creation is definitely not the right way. In "strict" mode — which all newly-written code should be using — it's an error.
SPOILER
I'm trying to solve problem #8 of this Javascript injection game.
In one of the comments by Erling Ellingsen, I found this interesting snippet.
(_=[].concat)()[0]
What's the difference between the above snippet, and this,
([].concat)()[0]
What changes when you assign [].concat to a variable? Clearly, he's just trying to access the global window object, but how do these two evaluate differently?
The reason why this previously worked was because it was specified in a previous version of ECMAScript that the this value would be the global object (i.e. window). However, from ECMAScript 5 onwards, the this value is now undefined, which make Array.prototype.concat throws an error.
In ES3, if a native function got called with a this value of undefined or null (such as when you call it using func()), it would provide the global object to the function.
In ES5, the behaviour of native functions has been changed so that it gets the actual undefined or null value, even though your code is not in strict mode.
The difference between the two is that one is the value of the function, and therefore is indirectly called, while the other is a reference to the function.
GetValue() is an internal function that gets the value of a reference from a variable - this is not called when calling "directly", but when assigning to another variable and using the result of that, it is indeed called (source).
An infamous example of the difference between the two is when using eval():
var a = 0;
function test()
{ var a = 1, b;
console.log(eval("a")); // 1
console.log((b=eval)("a")); // 0
}
test();
In your example though, it works like the following:
var a = [].concat;
// called as variable, not property
a(); // therefore it's global
// same as the following, as all these expressions call GetValue() interally
(0, [].concat)();
(random = [].concat)();
([].concat || 0)();
// but this doesn't work
[].concat(); // `this` is the new array, not global object
It's been a while since ECMAScript 5 came out and is being supported quite well in most modern browsers (IE9, CH 19+, FF 4+) and with it so is the "Immutable undefined". Though I keep seeing "undefined" being passed like so:
(function ( ..., undefined ) {
})(...);
From what I can tell Crockford's JSLint tool doesn't really like it:
Expected an identifier and instead saw 'undefined' (a reserved word).
Even though it's not being passed (there's no argument on the function call) and it's really an identifier. I understand that his tool isn't a bible we should follow to the death and on the other hand JSHint doesn't seem to care about it.
Is this still considered a best practice today and how does it affect code performance/security? Considering browser support >= IE9.
Is this still considered a best practice today and how does it affect code performance/security? Considering browser support >= IE9.
Much like undefined, Infinity is a non-writable property of the global object rather than a literal like null, so it can be used as a parameter name without raising an error. Lets consider a function like this
function example(Infinity) {
return Infinity;
}
In example, Infinity is no longer a non-writable property and instead acts like any other parameter to a function, initialised as undefined if nothing is passed or otherwise taking the value of the argument. This means in this function you can't just assume Infinity will mean Infinity, because it doesn't, but you could assume it will mean undefined.
So let's go back to the question of undefined. It will be initialised as undefined so the meaning will stay the same, however, it has now lost it's immutability within the function and therefore any typos assinging a values to undefined will have this error carried forward. Additionally, if a parameter is passed to the function the value will be different from the start.
Therefore to conclude, it does have an effect on security, as an undefined like this is no longer as "safe" because it has lost it's immutable status and can be changed.
It may be interesting to note that undefined is not actually a reserved word. If you require an undefined for compatibility, rather than adding it as a parameter name, I would suggest using a simple var undefined; at the top of the function or in the global namespace where you will find var undefined; undefined = 1; undefined; // undefined where it is immutable.
If you wanted to comply with jsLint, you could use another variable that is not a reserved word instead of undefined. The following complies with jsLint:
(function (window, document, undef) {
'use strict';
}(window, document));
You would, however, need to remember to use your newly defined variable (in this case undef instead of undefined). The strict mode above was added to comply with jsLint. This is mostly used so that you know for certain that the variable you are testing to be undefined is actually undefined.
Note, that if you're running this example in the text area provided on the jsLint website, you may need to define window and document. I was receiving two errors until I did so.
var window = window; //global window object
var document = document; //global window object
However, I'm of the opinion that:
(function (..., undefined) {
'use strict';
}(...));
is perfectly fine and more importantly, highly recommended.
There are cases where reserved words can be changed from their normal states. Such as setting undefined to true. Paul Irish discusses this briefly in his video: http://paulirish.com/2010/10-things-i-learned-from-the-jquery-source/