I'm trying to improve my understanding of the global namespace in javascript and I'm curious about a few things:
is there a "GOD" (i.e. a parent) object that all objects (since all things except primitives are objects) to answer to and if so would that object be "window" ?
why is it bad idea to have vars/functions on a global level?
if it is really a bad idea to have vars/functions in global scope then would closures be the best way to avoid this? example:
function parent(){
var x = 'some value';//this var would be considered global to all children functions but not in the true global namespace
function child1(){
x.someMethod()
}
function child2(){
x*something;
}
function child3(){
x+=something;
child2()
child1()
}
child3()
}
parent()
Is there a god (i.e. a parent) object?
Yes. More technically, it's the global object that all these primitives are members of; it just happens that in the browser, the window object is the global object.
> window.String === String;
true
Why is it bad idea to have vars/functions on a global level?
Because if you're adding lots of 3rd party libraries/ scripts, they all share the same global object, there's the chance of name collisions. This is a real life problem with all the libraries which use $ as an alias (jQuery, Prototype and more).
If it is really a bad idea to have vars/functions in global scope then would closures be the best way to avoid this?
x shouldn't be considered global. It's part of the closure formed by declaring the child functions inside the parent() function. The problem part of your snippet is that parent() is global; what happens if some other code re-declared parent()? This would be better:
(function () {
function parent(){
var x = 'some value';
function child1(){
x.someMethod()
}
function child2(){
x*something;
}
function child3(){
x+=something;
child2()
child1()
}
child3()
}
parent()
}());
The fact x is accessible within the child functions isn't bad; you should have written those functions yourself, so you should be aware of the existence of x. Bear in mind that if you re-declare x within those child functions with var, you won't affect the x in parent().
Yes, in a browser environment the "god object" is window. It's typically called the global object, not god object though ;) In non-browser environments such as nodejs, the global object may use some other name than window.
If you put everything as globals, you risk running into colliding names. There is also the matter of encapsulation - in other words, by putting variables into only the scope where it's needed, your code is usually better off.
Yep, this is pretty much the preferred approach. You can also use IIFE's
As far as I know, I'd say yes, window is the parent object. However, inside an Iframe you have your own window object, distinct from surrounding window which you can access through window.parent
It's a bad idea to have a LOT of global var because of potential name collision and therefore hard to detect bugs. In general it's safer to design some namespace (see
the $ from jQuery, etc) and modularize code.
Be careful, parent is a potential existing field of window. This taken appart, function are object so the same observation than in 2) apply here.
If you NEED to put variables in the global namespace, and you likely will at some point, create a single object variable and add your other variables to it as properties or methods. Give the object a name that is not likely to be used by anyone else (admittedly, this is where collision problems arise, but that can be mitigated by careful, standardized naming).
e.g. Instead of:
var thing1 = 'table';
var anotherthing = 'chair';
var mypet = 'dog';
var count = 4;
var show_something: function( _txt ) { return _txt.trim(); };
Do this:
var cmjaimet_obj = {
thing1: 'table',
anotherthing: 'chair',
mypet: 'dog',
count: 4,
show_something: function( _txt ) { return _txt.trim(); }
};
Then later call them as properties:
e.g. Instead of:
count += 2;
anotherthing = 'sofa';
console.log( show_something( 'Thing: ' + anotherthing ) );
Do this:
cmjaimet_obj.count += 2;
cmjaimet_obj.anotherthing = 'sofa';
console.log( cmjaimet_obj.show_something( 'Thing: ' + cmjaimet_obj.anotherthing ) );
Related
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();
I wrote a simple object destructuring function that declares variables in given scope through assigning properties to the scope object
Can I use this function in production and if not what are the pitfalls of using such a function?
function destructure(obj, scope) {
scope = scope || this;
Object.keys(obj).forEach(key => {
if (scope[key] === undefined) {
scope[key] = obj[key];
} else {
throw new Error(key + ' variable is already declared.');
}
});
}
var o = {
one: 1,
two: 2,
three: 3,
four: 4
};
destructure(o);
console.log(one); // 1
console.log(two); // 2
console.log(three); // 3
console.log(four); // 4
Are there any pitfalls of declaring variables through this keyword?
Yes: It doesn't really work.
There might be a misunderstanding about how this and scope work.
About scope
You can only declare variables by adding a property to an object is when the environment is backed by an actual object. This is only the case for two situations:
Global scope, which is (partially) backed by the global object (window in browsers)
The with statement
Examples:
// Global scope
window.foo = 42;
console.log(foo);
// with statement
var obj = {};
with (obj) {
obj.bar = 21;
console.log(bar);
}
Since you mention that you are using Node, I want to emphasize that function scope and module (which are just functions in Node anyway atm) scope are not backed by objects, thus there is no way to dynamically declare variables.
About this
The value of this depends on how a function is called. It could basically refer to any value.
Now, calling a(n unbound) function the "normal way", i.e. as f(), has this arbitrary behavior that this will refer to the global object, e.g. window in browsers. Of course being the global scope, any variable added there is accessible every else, but that's not necessarily a good thing.
So, while your solution may seem work, it really only works coincidentally when you run your code in global scope.
About strict mode
Strict mode changes some of the behaviors that were deemed incorrect or dangerous. Most importantly it changes how this inside normal function calls (f()) behaves. Instead of referring to the global object, it will simply be undefined. Thus if your function was declared in strict mode, your code would actually throw an error.
The with statement is also forbidden in strict mode.
With ES2016, new constructs such as classes and modules are strict by default, so you can see that that's the direction where the language is developing and you should not rely on behavior that doesn't work in strict mode.
declares variables in given scope through assigning properties to the scope object
No, that's not how a variable declaration works. Also, the only "scope object" that is available to JS - ignoring the with statement - is the global object (which you happened to use in your proof of concept), and you don't want to mess with that.
Can I use this function in production?
Absolutely not.
If you're sold on the idea, you probably can hack it with eval, but really you just should use real destructuring like everyone else:
var {one, two, three, four} = o;
This is the programmatic approach to:
this["one"] = 1
this["two"] = 2
this["three"] = 3
this["four"] = 4
While this theoretically fine, you may run into trouble by relying on this. It would be better to directly specify global, so that you don't run into trouble with .bind() and friends.
function destructure(obj, scope) {
scope = scope; // Remove the this
Object.assign(scope, obj);
}
destructure(o, global) // Explicitly declare global scope
As for memory leaks, performance, etc. (which I feel this question is more about), you have to remember that everything that you assign to global never gets Garbage Collected. You should really only add functions to global, and that's only if you really have to.
Why not use just Object.assign?
function destructure(obj, scope) {
scope = scope || this;
Object.assign(scope, obj);
}
I have one question and don't know do I do right thing.
I define window and document in objects or global variable in my pluins like this:
var myplugin = {
document : document || {},
window : window || {}
};
After I call that when I need like this:
myplugin.window.location.href = 'http://flyflyflymachine.etc';
And I would like to know do I get some better performance or is unnecessarily?
As for performance, yes caching can help but you are not caching anything here. Merely replacing one lookup with another one (yourplugin.document vs window.document).
Actually in that case, just document is faster, but unless you are in a tight loop calling it a few thousand times, don't bother with these kind of nano optimisations.
In theory this kind of nano-optimization is more appplicable to nested functions created when a plugin is defined in terms of an IIFE instead of using an object literal initializer.
When code references a variable of object, the javascript engine has to search the current lexical scope record first, followed by any parent lexical scrope records until the variable or object definition is either found or proven not to have been defined.
Lexical scope records are created for for loops using let variable definitions and for functions to hold arguments and variables. So in a deeply nested for loop, in a deeply nested function, there could be a number of scope records to inspect before finding either window or document.
Making a copy of a global or outer function reference could reduce the number of scope records to search. A net performance gain will only arise if the overhead of making the copy is less than the overhead of the JavaSript engine searching the intermediary scope records, multiplied by the number of times the outer reference is used. This may be both rare in practice and difficult to prove if the gain is small.
The kind of plugin definition in the post, consisting of an "Object object" intializer instead of an IIFE does not meet the criteria of speeding up object reference searching and, indeed, could actually slow things down.
You seem to be concerned about window or document not existing. If, for example, this code were run from inside a Worker or NodeJS script, it would throw an error.
So you could "optimize" this in two ways:
Use a namespace with the this keyword (from global scope, this will refer to the global object)
var myPlugin = {};
(function myPluginNS(_global) {
myPlugin.window = _global;
myPlugin.document = _global.document || {};
})(this);
Use good ol' fashioned try-catch
var myPlugin = {};
try {
myPlugin.window = window;
} catch (e) {
myPlugin.window = {};
}
try {
myPlugin.document = document;
} catch (e) {
myPlugin.document = {};
}
Admittedly, not really an "optimization", but it will at least ensure your code can run in various JS run-times.
Lets define a dog:
function Dog() {}
Now lets define a self executing function that passes the Dog as a parameter:
(function(dogClassName) {
var someServerSideLogicResolvesClassName = "dogClassName";
//Iniate class name from the server side string
})(Dog);
After some googleing I found that I should use
new window["dogClassName"]();
or
new this["dogClassName"]();
But since it's a scoped variable it doesn't exist in the global name space (no window) and this is undefined; I have no idea how to achieve this without using some sort of evaling.
Right, assuming that you're trying to access a constructor that isn't set globally, then the answer to your question is, quite simply: you can't do this. The scopes of closures are managed differently by the various engines. V8 might even GC the constructor if the returned function doesn't reference it explicitly... There is no magic way to get a full outer scope as an object
The only solution would be to create a namespace-object:
(function()
{
var constructors = {Constructor1: function(){},
Constructor2: function(){}};
return function()
{
var className =getClassNameViaAjax();
if (constructors.hasOwnProperty(className))
{
return new constructors[className]();
}
if (window[className] instanceof Function)
{//fallback
return new window[className]()
}
throw className + ' Does not exist in closure, nor in global scope';
};
}());
Have a look at this question, it seems to me to be related, or even a duplicate.
Hesitant update, but for completeness
There is one way you can use a variable's value as reference in the scope chain, but it does require the most Evil of Evil functions: eval('new '+ className + '()'), as you can see in the specification, eval will preserve the this bindings, and reinitialize the scope chains. Except when you're using 'strict mode';Since you're getting the constructor name from an ajax response, and eval is an easy thing to exploit, don't do this! This is a sort of an "in theory, you could..." thing.
almost all javascript books said that
always use var keyword when you
declare variables, because without
var, the variable will be declared as
global variable.
then, why not remove var keyword, make default declaration as local scope?
like Python, if you want to use global variables, you write:
global foo;
we use local variables almost all the time, don't we?
is there a good reason?
Thanks for any help.
edit:
Thanks for all your help, I thought there must be a good reason shows that using var is better, so I was not attempting to change the language what it was like.
var globalInt = 5;
function Hello()
{
// Hey, give me back my var keyword!
// I want to assign a global existing variable
globalInt = 7;
}
Another point is there is no easy way to remove something from JavaScript. Every feature (even hacks) is already used on thousands of sites and removing features will break those sites. JavaScript can only be extended. Or new JS should be created which will be incompatible with previous versions.
function A() {
var foo; // variable in the scope of function A
function B() {
foo; // variable still in the scope of function A
}
}
If the choice was "Narrowest scope" or "Global scope" then this wouldn't be possible.
This is just the way the language was designed. If a variable is auto-allocated it is allocated in the global scope.
It makes a lot of sense if you think about it. What scope should a variable be allocated in? The compiler has no way of knowing the programmers goal with an explicit declaration
For better or worse JS was designed the way it was. I believe allowing variables to auto-declare was a mistake but given that it exists in JS it makes sense to have them be global since JS does not have block level scope.
using var keyword inside a function declares the variable in local scope, hence preventing overwriting of any global variable. Idea is to play safe, and use var. If you know what you are doing (100% sure that you will not overwrite any global variable) feel free to discard var.
OK, i'll try to explain how it works again. There is a ECMAScript's Global object, which is "root" of everything else. In browsers window object implements Global. So:
function assert( condition, message ) {
if ( !condition )
if ( typeof message != 'undefined' ) alert( message );
else alert( 'Free Consulting just talks rubbish' );
}
// assuming global scope
assert( this == window, 'noes!' ); // and Global context
var spam = 'for all you python lovers'; // becomes a property of Global
assert( spam == window.spam, 'there not much spam in it' ); // same
function eggs () { // becomes a method of Global actually
assert( spam == window.spam, 'shut up!' ); // unqualified spam available here through closure
assert( arguments.callee == window.eggs ); // and again
}
eggs();
Mrs Conclusion: JavaScript is distinct language with own specific traits, so do not apply other language knowledge to JS (it makes Douglas Crockford a sad panda :)