Why are global variables added to a window object in JavaScript?
var a = 1;
console.log(window.a);
ECMAScript 2015 language specification does not say that declared var or function is added to window global objects.
However, I would like to know why the global variable declared var is added as a property to the window object.
What I'm curious about is that it has nothing to do with ECMAScript language specification?
I used a translator, so please understand if it's weird.
It actually is noted in the specification here, when a variable is assigned to when no such identifier exists:
If IsUnresolvableReference(V) is true, then
a. If V.[[Strict]] is true, throw a ReferenceError
exception.
b. Let globalObj be GetGlobalObject().
c. Return ? Set(globalObj, V.[[ReferencedName]], W, false).
And here, in CreateGlobalVarBinding, which is called when a variable is declared at the top level of a script.
6. Let varDeclarations be the VarScopedDeclarations of script.
...
18. For each String vn of declaredVarNames, do
a. Perform ? env.CreateGlobalVarBinding(vn, false).
The global object may be the window in a browser, or self in a web worker, or the Node global, etc.
I think the reason why var causes properties to be added to the global object (in the example you provided) has to do with how environments are linked in Javascript; they are linked by outer references, yet the global environment has no outer reference. Perhaps the choice to bind them to the global object, as properties, stems from this lack of outer reference in the global environment.
The global scope is the “outermost” scope – it has no outer scope. Its
environment is the global environment. Every environment is connected
with the global environment via a chain of environments that are
linked by outer references. The outer reference of the global
environment is null.
Source: https://2ality.com/2019/07/global-scope.html#the-global-object
Related
In the website the following lines are given:
Your global variables (or functions) can overwrite window variables (or functions).
Any function, including the window object, can overwrite your global variables and functions.
I am unable to understand what this says.
Above that paragraph you can find this:
Global Variables in HTML
With JavaScript, the global scope is the complete JavaScript environment.
In HTML, the global scope is the window object. All global variables belong to the window object.
Combined with the quote in your question - it means that because the global scope is actually the window object - it's possible to override global functions/variables.
Here is an example:
console.log(window.Math.PI);
Math = {
PI: 5
}
console.log(Math.PI);
The browser puts certain "global" (i.e. accessible anywhere in any file) functions and variables on the window object. One such example is the function window.setTimeout, which executes its argument after a given delay.
You can also access these global window.* variables without the window prefix at all, i.e.
window.setTimeout === setTimeout
That means if you assign to a global variable with a conflicting name, you can 'override' the browser defaults -
window.setTimeout === setTimeout
setTimeout = 'myString'
window.setTimeout === 'myString'
That's why it's generally best practice not to create variables in the global (window) scope.
var foo = 'bar';
console.log(window.foo); // bar
Seems like variables get assigned as properties to this, but inside anonymous functions, this refers to the parent scope, but doesn't assign variables to the parent scope.
function() {
var foo = 'bar';
}();
window.foo; // undefined
What object do variables get assigned to in non-global scopes?
To cite http://perfectionkills.com/understanding-delete/#execution_context:
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.
When control enters execution context for Global code, a Global object
is used as a Variable object. This is precisely why variables or
functions declared globally become properties of a Global object
Yet, these Variable Objects are not accessible. The only non-internal one is the global object, window or this (in global context).
The relevant section in the specification is #10: Executable Code and Execution Contexts.
In JavaScript, all variables are assigned to some scope object. However, only the scope object of global variables is accessible in JavaScript in the browser through the window object. Variables in a function scope are assigned to some scope object used internally by the JavaScript runtime, but this cannot be accessed by the user.
In another environment, global variables may be accessible as properties of another object (such as GLOBAL in node.js) or may be inaccessible (such as application scripts running inside the Windows Script Host).
They're available only in the function they're declared in.
Function scope is the only other scope in JavaScript, btw, unlike block-scoping in other {} languages.)
Re: your edit Don't be fooled--JS's this semantics are a bit irksome IMO--this may not be what you expect under a variety of circumstances.
Inside self-invoking anonymous function eg:
function() {
....
}()
All variables remain inside it and do not attach themselves to global object or window. Using that technique, there are patterns created such as module/singleton pattern.
Notice that in JS, variables have function-level scope.
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.
As said in sec 10.4.3
The following steps are performed when control enters the execution
context for function code contained in function object F, a caller
provided thisArg, and a caller provided argumentsList:
If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the global object.
Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).
Else set the ThisBinding to thisArg.
Let localEnv be the result of calling NewDeclarativeEnvironment passing the value of the [[Scope]] internal property of F as the
argument.
Set the LexicalEnvironment to localEnv.
Set the VariableEnvironment to localEnv.
Let code be the value of F‘s [[Code]] internal property.
Perform Declaration Binding Instantiation using the function code code and argumentsList as described in 10.5.
Consider the following code snippet:
function foo(){
var a={p:'p'};
o={c:'c'};
}
Thus we have the following:
Code of our function isnt a strict code
thisArg is null hence, ThisBinding set to the global object
---
---
I dont understand what bindings will be contains environment record represented by [[Scope]] internal property.
Set the LexicalEnvironment to environment which geted at step 5.
Set the VariableEnvironment to environment which geted at step 5.
Perform declaration binding instatiation.
At step 8 bindings are created in the VariableEnvironment, but not in LexicalEnvironment. But in sec 10.3 said that
When an execution context is created its LexicalEnvironment and
VariableEnvironment components initially have the same value.
Question:
Why just after creation of execution context LexicalEnvironment and VariableEnvironment is still equal in my case above?
I am not sure I understand your question, but this is how I understand sec 10.4.3 :
steps 1 to 4 are dealing with the value of this. Basically, in strict mode, this will be left to a null or undefined value instead of defaulting to the global object (window in case of a browser). This covers the cases when a function is not invoked through usual object or event handler mechanisms.
step 5 to 7 mean that a new naming environment is created each time you enter a function.
It describes the creation of this environment, which is linked to the previous one to form the current name scope.
For each new function, two environments are co-existing.
When a name is resolved, Lexical environment is searched first, then Variable environment. If both searches fail, the process is repeated at the upper level of the environment chain, until the 'catch-all' global scope is encountered. In this scope, all identifiers are handled as properties of the global (window) object. You can picture it as the whole code being enclosed in a with (window) block.
Lexical environment can be seen as a temporary augmentation of the variable scope.
Lexical and Variable environments are functionnally identical until you alter the lexical environment with two specific statements: with or catch. It does not mean they are implemented as identical data structures.
In terms of implementation, you could imagine the lexical environment as an empty list and the variable environment as a list containing all local variable and parameter names.
When a catch or with statement is encountered, the lexical list is populated with new names that will take precedence over the ones stored in the variable list.
catch will simply make its argument available for name resolution (i.e. allow you to reference the exception parameter). No big deal since the new name is just as explicit as a function parameter.
with is a rather more dangerous beast. It will create a new environment with the names of all properties of the object passed as its argument. The scope will consist of the chain of variable environments plus this new lexical environment.
Here the new names available for resolution are 'hidden' inside the object.
For instance:
var a = 'a', b = 'surprise!', o = {a:'a'};
with (o) { a = b; }
console.log (a+" "+b+" "+o.a);
will yield
a surprise! surprise!
a is resolved as o.a since o contains a property named a.
b is not found inside the lexical environment and thus the current variable environment is tried and variable 'b' is found.
This is a pretty dangerous mechanism, because if you believe an object contains a given property while it actually doesn't, you will instead reference a variable outside the current scope.
For instance, a simple typo like this:
with (element.style) {leftt = '10px';}
will set the window.leftt property to '10px', unless you happen to have declared a variable named leftt somewhere in the current scope.
Now if you give silly names like 'i' or 'j' to your object properties, chances are you will clobber a random loop index somewhere up the scope chain while believing you are setting the property of an object.
step 8 describes the parameters binding once the function scope is established. Basically, the parameters are bound with the values and their names are added to the variable environment.
The whole point of keeping two separate environments is that the hoisting mechanism always uses the variable environment chain as a scope.
The idea is that a variable or function should behave as if it had been declared at the top of the current scope block, so for instance a function declared inside a with block should not resolve its names with the object properties exposed by the with statement.
Frankly this is a rather moot point, since ECMA spec does not allow function declarations inside blocks, although most implementations do, with varrying results.
Now for your example:
function foo(){
var a={p:'p'};
o={c:'c'};
}
Your function does not contain any with or catch statements, so the scope chain inside 'foo()' is just a list of two variable environments :
global (a bunch of DOM objects all seen as properties of 'window')
function foo (var a)
once you call foo(),
a will resolve as foo's local variable, will be created as an object with the property p of value 'p' (and garbage collected as soon as you leave foo(), unless you manage to reference it from a persistent variable).
o will not be found in foo's variable environment, so it will be caught by the 'catch-all' global scope and thus resolved as a (new) property of the window object. It will create a window.o.c property with the value 'c'.
Does that answer somewhat to your question?
var foo = 'bar';
console.log(window.foo); // bar
Seems like variables get assigned as properties to this, but inside anonymous functions, this refers to the parent scope, but doesn't assign variables to the parent scope.
function() {
var foo = 'bar';
}();
window.foo; // undefined
What object do variables get assigned to in non-global scopes?
To cite http://perfectionkills.com/understanding-delete/#execution_context:
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.
When control enters execution context for Global code, a Global object
is used as a Variable object. This is precisely why variables or
functions declared globally become properties of a Global object
Yet, these Variable Objects are not accessible. The only non-internal one is the global object, window or this (in global context).
The relevant section in the specification is #10: Executable Code and Execution Contexts.
In JavaScript, all variables are assigned to some scope object. However, only the scope object of global variables is accessible in JavaScript in the browser through the window object. Variables in a function scope are assigned to some scope object used internally by the JavaScript runtime, but this cannot be accessed by the user.
In another environment, global variables may be accessible as properties of another object (such as GLOBAL in node.js) or may be inaccessible (such as application scripts running inside the Windows Script Host).
They're available only in the function they're declared in.
Function scope is the only other scope in JavaScript, btw, unlike block-scoping in other {} languages.)
Re: your edit Don't be fooled--JS's this semantics are a bit irksome IMO--this may not be what you expect under a variety of circumstances.
Inside self-invoking anonymous function eg:
function() {
....
}()
All variables remain inside it and do not attach themselves to global object or window. Using that technique, there are patterns created such as module/singleton pattern.
Notice that in JS, variables have function-level scope.