Understanding how Javascript resolving variables globally - javascript

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

Related

Is a Local Reference to the Global Scope Itself Fundamentally a Bad Idea?

Forgive my ignorance or paranoia if this is a stupid question, but I can't seem to find anything on this specific situation in particular (which leads me to think maybe most people either know to avoid it or have better ways to handle this).
Here's the premise: I don't know whether or not my code will necessarily always be ran in a browser, and as such, I don't know if I'll have access to window (specifically by that name) or if that scope will be named something else in whatever environment my code may find itself in.
So my idea was to find that scope once, assign it to a local variable then let everything within my function just reference that local variable if I ever need to access the global scope instead of reaching for window (again, it could be called something else—I won't know).
My concern is, is this going to cause some memory leak issue (or maybe it's a bad idea for other reasons)? Is there something I should be mindful of or am I good to go?
function myConstructor() {
// Is this fundamentally a bad idea?
var globalScope = findGlobalScope() // Cyclic reference here?
function findGlobalScope() {
// Let's say I had some logic here that
// determinded that I'm running this code
// in a browser, so now I know `window`
// is where my global variables are.
// Keep in mind it may not always be `window`,
// but let's say this time it is.
return window
}
this.doWork = function() {
// In here I reference the `globalScope` variable
// instead of the `window` variable explicitly.
//
// I'm doing it this way because I don't want
// my code to break if I run it outside of a browser
// where I might not have access to an object explicitly
// named "window"
console.log(globalScope) // instead of console.log(window)
}
}
var myObj = new myConstructor()
// Now `myObj` is located at `window.myObj`
// and `myObj` has a reference to `window`
// which... again, has a reference to window.myObj
// Is this something I should be concerned about?
I appreciate the help.
It's not a scope, it's an object. (But it's understandable you'd think of it as a scope; it's used by one of the global scopes to hold variables.¹)
That's absolutely fine, it's not going to cause leaks or anything. That's one object that you know won't be going away (unless your code does, too).
Note that JavaScript now has globalThis, which is exactly what you're looking for. :-) It's a relatively recent addition.
That said, as Taplar mentioned, ideally your code shouldn't care about the global object, and should avoid creating global variables entirely if possible. The global namespace is crowded — and extremely crowded on browsers.
Ways to avoid using global scope:
Use modules. Modern environments support ES2015's modules. Older environments can be supported via bundlers like Webpack or Rollup.js.
If you can't use a module, use a wrapper function. If necessary because your code must be split across files at runtime, have that function expose a single obscurely-named global that has properties on it for your other things.
Example of #2 (using ES5 syntax):
var myStuff = myStuff || {};
(function() {
myStuff.doSomething = function() {
// ...
};
})();
¹ This is an interesting, but confusing, part of JavaScript: There are at least two global scopes in JavaScript, and outer one and an inner one (which is new in ES2015+).
The outer one has "variables" that are properties of the global object, either inherited ones or own properties: If you use a variable name, and it doesn't exist in the current scope or one between the current scope and the global scope, but does exist as a property on the global object, the property on the global object is used — an old-style global variable. When you declare a function at global scope, or declare a variable with var, it adds a property to the global object. The global object also (in most environments) inherits things from its prototype and its prototype's prototype. For instance, globals created on browsers for elements with an id are properties of the WindowProperties object, which is in the prototype chain of the global object. It also sometimes has other "own" properties, such as name (the name of the window on browsers).
The inner global scope doesn't use the global object. It's where things declared with let, const, and class go. They don't become properties of the global object.

The confusion about Global object in JavaScript

I am beginner at JavaScript and I am trying to understand the Global window object in JavaScript. So, is it ok if I just imagine that any code that I write in the console like var text = "Hello"; console.log(text) is put inside window object like this Window{var text = "Hello"; console.log(this.text)} with this referencing window object. Is it ok if I consider it like that or it is not correct? Thank you
Is not pretty safe to make assumptions about the global object or the default this context, as it can vary from one javascript runtime to another, and some features like the strict mode also change this behavior.
Keep in mind that javascript not only runs in the browser -and global in node.js for instance does not work as in the browser-, and that there are a lot of different browser implementations out there.
Also, while var does write to global by default in some environments, const and let doesn't.
In node, functions called freely with no previous reference won't call them from global, but will fail instead. This heavily affects also front-end code since much of javascript for the browser nowadays is pre-compiled in a node environment via webpack etc.
So, succintly: is usually difficult to assume things about global, window and default this bindings and get them right. It is probably safer to assume that you don't have a default global object available and always refer window explicitly, as:
config.js
window.config = {foo: 'bar'}
window.someGlobalFunction = function() {...}
user.js
// do:
const elen = new User(window.config);
window.someGlobalFunction();
// don't
const elen = new User(config);
someGlobalFunction();
"is it ok to use the global object?"
Of course it is. If you know about the pitfalls and use the global object by purpose and not by accident.
(function() {
name = 12; // Note: This *is* a global variable
console.log(typeof name); // string. WTF
})();
The pitfalls are:
1) All undeclared (!) variables, and global variables declared with var automatically get part of the global object. That is bad, as that happens without any good reason, so you should avoid that (use strict mode, and let and const).
2) All scripts you run do have the same global object, so properties can collide. Thats what happens in the example above, name collides with the global window.name getter / setter pair that casts the number to a string. So if you set properties in the global object, make sure that the name is only used by you, and not by others (the browser, libraries, other codepieces you wrote ...)
If you know about these pitfalls and avoid them, you can and should use the global object by purpose, if, and only if, you plan to share a certain function / variable between different scripts on the page, so if it should really be globally accessible.
let private = 1;
window.shared = 2;
Yes, any function or variable can be accessed from the window object example:
var foo = "foobar";
foo === window.foo; // Returns: true
function greeting() {
console.log("Hi!");
}
window.greeting(); // It is the same as the normal invoking: greeting();

How to declare global variables in javascript the right way?

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.

Why does the JavaScript "delete" operator behave differently in different browsers?

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]]

Can I Use an ID as a Variable Name?

I find it convenient to set a variable with the same name as an element's id, for example:
randomDiv = document.getElementById("randomDiv");
randomDiv.onclick = function(){ /* Whatever; */ }
randomDiv.property = "value";
This works in Chrome and Firefox, but not IE8; giving the error Object doesn't support this property or method.
Is creating a variable with a name that matches an element ID wrong (or bad practice) or is this another instance of Internet Explorer acting up?
Making global variables automatically is considered bad practice because it can be difficult to tell, looking at some code, whether it is on purpose or you forgot to declare a variable somewhere. Automatic creation of global variables like this doesn’t work in ES5 strict mode and could be phased out phased out in future versions of ECMAScript.
In the browser JavaScript’s global scope is actually window. When you refer to document you get window.document. Best practice for creating a global variable in a browser is to add it to window (global in Node.js). Here’s an example from jQuery:
window.jQuery = window.$ = jQuery;
Some properties on window (hence some global variables) are read-only, you can’t overwrite them. window.document is one (tested in Chrome, this is all browser-specific and could change):
window.document; // → Document
window.document = 'foo'; // → "foo" // It worked!
window.document; // → Document // Hmm, no it didn’t
It turns out that most browsers create properties on window (hence global variables) for each id in the document. Many browsers don’t make them read-only, you can overwrite them with your own, but Internet Explorer does.
This is another reason global variables in JavaScript can be dangerous — one of your ids could match a read-only window property (today or in some future browser).
At the top level (not inside a function), var declares global variables. Stating var document = 'foo' at the top level won’t throw an error but document will still be the Document, not "foo".
As an aside: new-ish browsers (which support ES5) let you create your own read-only globals with Object.defineProperty:
Object.defineProperty(window, 'foo', { value: 'bar', writable: false });
foo = 'baz';
foo; // → "bar"
I’ve got three options for you.
Keep using global variables for your elements but leave them alone if they already exist (creating them on window explicitly so the code is clear and cool with ES5):
if ( ! window.randomDiv) {
window.randomDiv = document.getElementById('randomDiv');
}
Create an object, on window, to use as your app’s own namespace which won’t interfere with other libraries or with the browser. This is common and considered pretty good practice, especially if it needs to be accessed across JavaScript files:
// Early in your code…
window.Fantabulum = {};
// Later on…
Fantabulum.randomDiv = document.getElementById("randomDiv");
Avoid making globals. Make sure that your application’s code is inside a function (it should be already so your other variables aren’t global and don’t have the same limitations!), and declare variables for your elements:
(function(){
var randomDiv = document.getElementById("randomDiv");
})();
It is a quirk of IE to create global variables with the same name as element ids and names. You can create a global with the same name, but there are quirks with that.
It's a pretty awful idea. If you don't like typing document.getElementById, just make a small wrapper function for it such as:
funciton get(id) {
return typeof id == 'string'? document.getElementById(id) : id;
}
randomDiv is not an defined / known "global scope variable".
Declaring global variable
It seems the id becomes automatically a variable! No need to declare it:
<button id="b1">press me</button>
<script>
b1.onclick = function(){alert("Thanks!")}
</script>
As you see, it works (at least firefox 68) and does not throw any error.
Some more info:
Why don't we just use element IDs as identifiers in JavaScript?
JavaScript document.getElementById(“id”) and element id attribute

Categories