The confusion about Global object in JavaScript - 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();

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.

JavaScript - global namespace, window or nothing?

I'm using some libraries (like for example Less.js & Dojo) which require global config variables. For example:
less = { ... };
dojoConfig = { ... };
This works ok, but I'm wondering, should I declare this variables explicitly on window?
window.less = { ... };
window.dojoConfig = { ... };
What are pros & cons of each approach? what are pros & cons of referencing this variable from the actual code like (not considering possible name conflicts with local variables):
var somethingNew = dojoConfig.something;
The only thing I can think of is that code without window is prettier :)
If you start running your code in strict mode, you'll find that it's illegal to create implicit globals by excluding var or window..
IMO, it's just always a better practice to make your declarations explicit. For globals, that's either window.foo or var foo if you're in the global variable scope.
As far as referencing the existing global, it's just like any other variable at that point, so you don't really need window. for that purpose, though you could use it if the variable is shadowed by a local variable with the same name.
Explicitly attaching properties to window is easier to maintain than implicit global variable declarations. If you need to be flexible about the environment, you can use the following syntax:
//Here, "this" refers to the window when executed inside the browser
(function() { this.prop = "value" })()
console.log(window.prop === "value") // true
Within that function, you can add your private logic and expose the needed variables as needed. Also, you can name that function and attach those properties to any object you bind it to via function.prototype.bind
By the way, implicit globals also won't work in strict mode.

JavaScript identifiers not to use

I just found out the hard way that naming your variable arguments is a bad idea.
var arguments = 5;
(function () {
console.log(arguments);
})();
Output: []
It turns out that arguments is "a local variable available within all functions" so in each new execution context, arguments is shadowed.
My question is: Are there any other such treacherous names which, like arguments, are not true reserved words, but will cause still problems?
Yes. Like window or document, for example. See a longer list here ("other javascript keywords").
Wouldn't recommend using any of them, even though some would work as intended.
Edit: Like mentioned in javascript.about.com, "While they are not reserved words, the use of those words as variables and functions should be avoided.". They are listing mostly the same things classified as predefined classes and objects and global properties.
Example of a problem:
var window = 5;
(function () {
alert(window);
})();
the code above has unpredictable results due to fact that window is the word to refer to the window object. Firefox prevents modifications to it, so alert will still refer to window object, whereas in IE8, you'll get alert with value 5.
There are no other automatic symbols inside functions (apart from this, but you cannot create a variable with that name anyway), if that's what you mean. But there are a couple of default global variables, so defining a variable with the same name inside the function will shadow the global variable.
You can get a list of all names used in the global namespace, by executing this in the console:
var t = [];
for(v in this){t.push(v)}
console.log(t.sort());
Result:
["$","ArrayBuffer","Attr","Audio","AudioProcessingEvent","BeforeLoadEvent","Blob","CDATASection","CSSCharsetRule","CSSFontFaceRule","CSSImportRule","CSSMediaRule","CSSPageRule","CSSPrimitiveValue","CSSRule","CSSRuleList","CSSStyleDeclaration","CSSStyleRule","CSSStyleSheet","CSSValue","CSSValueList","CTIsPlayback","CanvasGradient","CanvasPattern","CanvasRenderingContext2D","CharacterData","ClientRect","ClientRectList","Clipboard","CloseEvent","Comment","CompositionEvent","Counter","CustomEvent","DOMException","DOMImplementation","DOMParser","DOMSettableTokenList","DOMStringList","DOMStringMap","DOMTokenList","DataView","DeviceOrientationEvent","Document","DocumentFragment","DocumentType","Element","Entity","EntityReference","ErrorEvent","Event","EventEmitter","EventException","EventSource","File","FileError","FileList","FileReader","Float32Array","Float64Array","FormData","Generator","HTMLAllCollection","HTMLAnchorElement","HTMLAppletElement","HTMLAreaElement","HTMLAudioElement","HTMLBRElement","HTMLBaseElement","HTMLBaseFontElement","HTMLBodyElement","HTMLButtonElement","HTMLCanvasElement","HTMLCollection","HTMLDListElement","HTMLDataListElement","HTMLDirectoryElement","HTMLDivElement","HTMLDocument","HTMLElement","HTMLEmbedElement","HTMLFieldSetElement","HTMLFontElement","HTMLFormElement","HTMLFrameElement","HTMLFrameSetElement","HTMLHRElement","HTMLHeadElement","HTMLHeadingElement","HTMLHtmlElement","HTMLIFrameElement","HTMLImageElement","HTMLInputElement","HTMLKeygenElement","HTMLLIElement","HTMLLabelElement","HTMLLegendElement","HTMLLinkElement","HTMLMapElement","HTMLMarqueeElement","HTMLMediaElement","HTMLMenuElement","HTMLMetaElement","HTMLMeterElement","HTMLModElement","HTMLOListElement","HTMLObjectElement","HTMLOptGroupElement","HTMLOptionElement","HTMLOutputElement","HTMLParagraphElement","HTMLParamElement","HTMLPreElement","HTMLProgressElement","HTMLQuoteElement","HTMLScriptElement","HTMLSelectElement","HTMLSourceElement","HTMLSpanElement","HTMLStyleElement","HTMLTableCaptionElement","HTMLTableCellElement","HTMLTableColElement","HTMLTableElement","HTMLTableRowElement","HTMLTableSectionElement","HTMLTextAreaElement","HTMLTitleElement","HTMLTrackElement","HTMLUListElement","HTMLUnknownElement","HTMLVideoElement","HashChangeEvent","IceCandidate","Image","ImageData","Int16Array","Int32Array","Int8Array","KeyboardEvent","Markdown","MediaController","MediaError","MediaList","MediaStreamEvent","MessageChannel","MessageEvent","MessagePort","MimeType","MimeTypeArray","MouseEvent","MutationEvent","NamedNodeMap","Node","NodeFilter","NodeList","Notation","Notification","OfflineAudioCompletionEvent","Option","OverflowEvent","PERSISTENT","PR","PR_SHOULD_USE_CONTINUATION","PageTransitionEvent","Plugin","PluginArray","PopStateEvent","ProcessingInstruction","ProgressEvent","RGBColor","RTCIceCandidate","RTCSessionDescription","Range","RangeException","Rect","SQLException","SVGAElement","SVGAltGlyphDefElement","SVGAltGlyphElement","SVGAltGlyphItemElement","SVGAngle","SVGAnimateColorElement","SVGAnimateElement","SVGAnimateMotionElement","SVGAnimateTransformElement","SVGAnimatedAngle","SVGAnimatedBoolean","SVGAnimatedEnumeration","SVGAnimatedInteger","SVGAnimatedLength","SVGAnimatedLengthList","SVGAnimatedNumber","SVGAnimatedNumberList","SVGAnimatedPreserveAspectRatio","SVGAnimatedRect","SVGAnimatedString","SVGAnimatedTransformList","SVGCircleElement","SVGClipPathElement","SVGColor","SVGComponentTransferFunctionElement","SVGCursorElement","SVGDefsElement","SVGDescElement","SVGDocument","SVGElement","SVGElementInstance","SVGElementInstanceList","SVGEllipseElement","SVGException","SVGFEBlendElement","SVGFEColorMatrixElement","SVGFEComponentTransferElement","SVGFECompositeElement","SVGFEConvolveMatrixElement","SVGFEDiffuseLightingElement","SVGFEDisplacementMapElement","SVGFEDistantLightElement","SVGFEDropShadowElement","SVGFEFloodElement","SVGFEFuncAElement","SVGFEFuncBElement","SVGFEFuncGElement","SVGFEFuncRElement","SVGFEGaussianBlurElement","SVGFEImageElement","SVGFEMergeElement","SVGFEMergeNodeElement","SVGFEMorphologyElement","SVGFEOffsetElement","SVGFEPointLightElement","SVGFESpecularLightingElement","SVGFESpotLightElement","SVGFETileElement","SVGFETurbulenceElement","SVGFilterElement","SVGFontElement","SVGFontFaceElement","SVGFontFaceFormatElement","SVGFontFaceNameElement","SVGFontFaceSrcElement","SVGFontFaceUriElement","SVGForeignObjectElement","SVGGElement","SVGGlyphElement","SVGGlyphRefElement","SVGGradientElement","SVGHKernElement","SVGImageElement","SVGLength","SVGLengthList","SVGLineElement","SVGLinearGradientElement","SVGMPathElement","SVGMarkerElement","SVGMaskElement","SVGMatrix","SVGMetadataElement","SVGMissingGlyphElement","SVGNumber","SVGNumberList","SVGPaint","SVGPathElement","SVGPathSeg","SVGPathSegArcAbs","SVGPathSegArcRel","SVGPathSegClosePath","SVGPathSegCurvetoCubicAbs","SVGPathSegCurvetoCubicRel","SVGPathSegCurvetoCubicSmoothAbs","SVGPathSegCurvetoCubicSmoothRel","SVGPathSegCurvetoQuadraticAbs","SVGPathSegCurvetoQuadraticRel","SVGPathSegCurvetoQuadraticSmoothAbs","SVGPathSegCurvetoQuadraticSmoothRel","SVGPathSegLinetoAbs","SVGPathSegLinetoHorizontalAbs","SVGPathSegLinetoHorizontalRel","SVGPathSegLinetoRel","SVGPathSegLinetoVerticalAbs","SVGPathSegLinetoVerticalRel","SVGPathSegList","SVGPathSegMovetoAbs","SVGPathSegMovetoRel","SVGPatternElement","SVGPoint","SVGPointList","SVGPolygonElement","SVGPolylineElement","SVGPreserveAspectRatio","SVGRadialGradientElement","SVGRect","SVGRectElement","SVGRenderingIntent","SVGSVGElement","SVGScriptElement","SVGSetElement","SVGStopElement","SVGStringList","SVGStyleElement","SVGSwitchElement","SVGSymbolElement","SVGTRefElement","SVGTSpanElement","SVGTextContentElement","SVGTextElement","SVGTextPathElement","SVGTextPositioningElement","SVGTitleElement","SVGTransform","SVGTransformList","SVGUnitTypes","SVGUseElement","SVGVKernElement","SVGViewElement","SVGViewSpec","SVGZoomAndPan","SVGZoomEvent","Selection","SessionDescription","SharedWorker","SpeechInputEvent","StackExchange","Storage","StorageEvent","StyleSheet","StyleSheetList","TEMPORARY","Text","TextEvent","TextMetrics","TextTrack","TextTrackCue","TextTrackCueList","TextTrackList","TimeRanges","TouchEvent","TrackEvent","UIEvent","URL","Uint16Array","Uint32Array","Uint8Array","Uint8ClampedArray","WebGLActiveInfo","WebGLBuffer","WebGLContextEvent","WebGLFramebuffer","WebGLProgram","WebGLRenderbuffer","WebGLRenderingContext","WebGLShader","WebGLShaderPrecisionFormat","WebGLTexture","WebGLUniformLocation","WebKitAnimationEvent","WebKitBlobBuilder","WebKitCSSFilterValue","WebKitCSSKeyframeRule","WebKitCSSKeyframesRule","WebKitCSSMatrix","WebKitCSSRegionRule","WebKitCSSTransformValue","WebKitIntent","WebKitMediaSource","WebKitMutationObserver","WebKitPoint","WebKitSourceBuffer","WebKitSourceBufferList","WebKitTransitionEvent","WebSocket","WheelEvent","Window","WinterBash","Worker","XMLDocument","XMLHttpRequest","XMLHttpRequestException","XMLHttpRequestProgressEvent","XMLHttpRequestUpload","XMLSerializer","XPathEvaluator","XPathException","XPathResult","XSLTProcessor","__qc","_gaq","_gat","_qevents","addEventListener","alert","apiCallbacks","applicationCache","atob","blur","btoa","captureEvents","careers_adselector","careers_adurl","careers_companycssurl","careers_cssurl","careers_leaderboardcssurl","chrome","clearInterval","clearTimeout","clientInformation","close","closed","confirm","console","crypto","defaultStatus","defaultstatus","devicePixelRatio","dispatchEvent","document","event","external","find","focus","frameElement","frames","gaGlobal","gauth","genuwine","getComputedStyle","getMatchedCSSRules","getSelection","history","i","initFadingHelpText","initTagRenderer","innerHeight","innerWidth","jQuery","jQuery171005593172716908157_1357215797040","jQuery171005593172716908157_1357215797041","length","localStorage","location","locationbar","matchMedia","menubar","moveBy","moveScroller","moveTo","name","navigator","offscreenBuffering","onabort","onbeforeunload","onblur","oncanplay","oncanplaythrough","onchange","onclick","oncontextmenu","ondblclick","ondeviceorientation","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","onhashchange","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmessage","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onoffline","ononline","onpagehide","onpageshow","onpause","onplay","onplaying","onpopstate","onprogress","onratechange","onreset","onresize","onscroll","onsearch","onseeked","onseeking","onselect","onstalled","onstorage","onsubmit","onsuspend","ontimeupdate","onunload","onvolumechange","onwaiting","onwebkitanimationend","onwebkitanimationiteration","onwebkitanimationstart","onwebkittransitionend","open","openDatabase","opener","outerHeight","outerWidth","pageXOffset","pageYOffset","parent","performance","personalbar","postMessage","prepareEditor","prettyPrint","prettyPrintOne","print","profileLink","prompt","quantserve","releaseEvents","removeEventListener","resizeBy","resizeTo","sanitizeAndSplitTags","screen","screenLeft","screenTop","screenX","screenY","scriptSrc","scroll","scrollBy","scrollTo","scrollX","scrollY","scrollbars","self","sessionStorage","setInterval","setTimeout","showFadingHelpText","showModalDialog","status","statusbar","stop","styleCode","styleMedia","t","tagRenderer","tagRendererRaw","toolbar","top","uh","v","v8Intl","votesCast","webkitAudioContext","webkitAudioPannerNode","webkitCancelAnimationFrame","webkitCancelRequestAnimationFrame","webkitConvertPointFromNodeToPage","webkitConvertPointFromPageToNode","webkitIDBCursor","webkitIDBDatabase","webkitIDBDatabaseException","webkitIDBFactory","webkitIDBIndex","webkitIDBKeyRange","webkitIDBObjectStore","webkitIDBRequest","webkitIDBTransaction","webkitIndexedDB","webkitIntent","webkitMediaStream","webkitNotifications","webkitPostMessage","webkitRTCPeerConnection","webkitRequestAnimationFrame","webkitRequestFileSystem","webkitResolveLocalFileSystemURL","webkitStorageInfo","webkitURL","window"];
So, these can't / shouldn't be used (at least in Google Chrome), in the global scope. If you declare them as variables in a local scope, you're fine.
This won't contain all names used, however, since arguments is only available in the function scope, for example.
Note: this function does return all global variables from your libraries, also

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

How to preserve global variables in javascript when using Closure compiler with advanced optimization?

I have my own Javascript library, which I want to minify by using Google closure compiler with Advanced optimization. By looking at the docs I see how to declare functions which are used outside of the library.
However I couldn't find a way how to preserve global variables declared in my library. Closure compiler just removes them, because it thinks they are never used. Anybody can help ?
Edit: example code:
var variable_1 = true;
This is defined globally right at the beginning of my library, but it's never used in the library itself. It is used outside the library when it is included in some page. But that Closure compiler doesn't know and thats the reason it removes these declarations.
The closure compiler cannot remove global variables declared as window["variable_1"] = true
I recommend you write to window directly for global variables and I also recommend you use string literals for your variable names so that closure doesn't minify it.
Although you can refer to a "true" global variable through replacing all usage of that global variable with window["varname"], it is usually not a good idea to "pollute" the global namespace. The Closure Compiler is designed to discourage you from doing this.
CAVEAT: window["varname"] and var varname are not the same, as "window" may not always be the global object in non-browser environments. As a matter of fact, the Closure Compiler assumes that the global object and "window" are different. For example, window["varname"] will compile to window.varname instead of var varname. They are NOT the same, although in a browser they work similarly.
It is best to make a global namespace object, then only export that one object. All your "global" variables should become properties under this global namespace variable. Benefits:
All those global variables are renamed to shorter versions
Inlining of constants can happen
The Closure Compiler automatically "flattens" the namespace anyway, so your code won't be any slower
Superior obfuscation
Your code also works in non-browser environments. Remember, "window" may not always exists (e.g. in server-side code) and the "global object" may not always be "window"
If you have global variables that the user must read/set to use your library, it is also discouraged. It is better to expose an API on the global namespace object, then expose the public API's as usual through the window object: window["myLib"]["setConfig"] = myLib.setConfig.
In your case, if you have global variables used in other parts of your non-Closure-Compiled code, you have to consider:
Is it better to put the declaration of those variables outside of the file being compiled by Closure
Why are you not putting the declaration of those variables together with the code using them
Should you actually be Closure-compiling all the code instead of only a portion (it is possible? Do you use another library?)
I've just come across this, and I have my own solution.
Create you're entire library within a self-executing function, putting all the object properties as strings (at least once for each property) like so:
(function () {
var myLibrary = {
'myMethod' : function () {
...
}
}
myLibrary.myMethod['propertyOfTheMethod'] = '';
}());
The usual way to make this accessible from the outside would be to put var myLibrary = before the function and return myLibrary at the end of it so that it gets assigned to a global variable. But the function is executed in the global scope (because it's self-executing), so we can create a property of this using a string literal. So in full:
(function () {
var myLibrary = {
'myMethod' : function () {
...
}
}
myLibrary.myMethod['propertyOfTheMethod'] = '';
this['myLibrary'] = myLibrary;
}());
But, this won't work under "use strict";. The best way to get the global variable in strict mode is with var global = Function('return this')(); then assign your variable to that.

Categories