I need a few global variables that I need in all .js files.
For example, consider the following 4 files:
global.js
js1.js
js2.js
js3.js
Is there a way that I can declare 3 global variables in global.js and access them in any of the other 3 .js files considering I load all the above 4 files into a HTML document?
Can someone please tell me if this is possible or is there a work around to achieve this?
Just define your variables in global.js outside a function scope:
// global.js
var global1 = "I'm a global!";
var global2 = "So am I!";
// other js-file
function testGlobal () {
alert(global1);
}
To make sure that this works you have to include/link to global.js before you try to access any variables defined in that file:
<html>
<head>
<!-- Include global.js first -->
<script src="/YOUR_PATH/global.js" type="text/javascript"></script>
<!-- Now we can reference variables, objects, functions etc.
defined in global.js -->
<script src="/YOUR_PATH/otherJsFile.js" type="text/javascript"></script>
</head>
[...]
</html>
You could, of course, link in the script tags just before the closing <body>-tag if you do not want the load of js-files to interrupt the initial page load.
The recommended approach is:
window.greeting = "Hello World!"
You can then access it within any function:
function foo() {
alert(greeting); // Hello World!
alert(window["greeting"]); // Hello World!
alert(window.greeting); // Hello World! (recommended)
}
This approach is preferred for two reasons.
The intent is explicit. The use of the var keyword can easily lead to declaring global vars that were intended to be local or vice versa. This sort of variable scoping is a point of confusion for a lot of Javascript developers. So as a general rule, I make sure all variable declarations are preceded with the keyword var or the prefix window.
You standardize this syntax for reading the variables this way as well which means that a locally scoped var doesn't clobber the global var or vice versa. For example what happens here is ambiguous:
greeting = "Aloha";
function foo() {
greeting = "Hello"; // overrides global!
}
function bar(greeting) {
alert(greeting);
}
foo();
bar("Howdy"); // does it alert "Hello" or "Howdy" ?
However, this is much cleaner and less error prone (you don't really need to remember all the variable scoping rules):
function foo() {
window.greeting = "Hello";
}
function bar(greeting) {
alert(greeting);
}
foo();
bar("Howdy"); // alerts "Howdy"
Have you tried it?
If you do:
var HI = 'Hello World';
In global.js. And then do:
alert(HI);
In js1.js it will alert it fine. You just have to include global.js prior to the rest in the HTML document.
The only catch is that you have to declare it in the window's scope (not inside any functions).
You could just nix the var part and create them that way, but it's not good practice.
As mentioned above, there are issues with using the top-most scope in your script file. Here is another issue: The script file might be run from a context that is not the global context in some run-time environment.
It has been proposed to assign the global to window directly. But that is also run-time dependent and does not work in Node etc. It goes to show that portable global variable management needs some careful consideration and extra effort. Maybe they will fix it in future ECMS versions!
For now, I would recommend something like this to support proper global management for all run-time environments:
/**
* Exports the given object into the global context.
*/
var exportGlobal = function(name, object) {
if (typeof(global) !== "undefined") {
// Node.js
global[name] = object;
}
else if (typeof(window) !== "undefined") {
// JS with GUI (usually browser)
window[name] = object;
}
else {
throw new Error("Unkown run-time environment. Currently only browsers and Node.js are supported.");
}
};
// export exportGlobal itself
exportGlobal("exportGlobal", exportGlobal);
// create a new global namespace
exportGlobal("someothernamespace", {});
It's a bit more typing, but it makes your global variable management future-proof.
Disclaimer: Part of this idea came to me when looking at previous versions of stacktrace.js.
I reckon, one can also use Webpack or other tools to get more reliable and less hackish detection of the run-time environment.
Yes you can access them. You should declare them in 'public space' (outside any functions) as:
var globalvar1 = 'value';
You can access them later on, also in other files.
Related
With eslint you can declare a global variable with /* global <name-of-var> */. For example
/* global document */
const elem = document.querySelector(selector);
Is it possible to declare a global but only inside a certain scope?
function foo(selector) {
/* global document */
return document.querySelector(selector);
}
function bar(selector) {
return document.querySelector(selector); // error! document undefined
}
Testing the example above fails in that declaring document as a global in foo makes it accesseble in bar.
The advantage making the global's access scoped is that if you delete a function there is nothing else to clean up. It declares it's own access. Where as if you put the globals at the top of the file and delete a function you also have to go clean up the top of the file. It also arguably better documenting where the variable comes from based on the idea that more info locally is better.
Note: while I know the feature is different, python has the concept of giving an individual function access to a global. Doing so does not give all functions access to that global so the idea in general of declaring globals per function is not unusual.
Is there a way declare the existence of global only within a certain scope for eslint?
The documentation doesn't say anything about doing it only locally in a function. I can see the point of what you're describing; you never know, they might be open to a pull request adding that...
Two workarounds for you:
Group together the functions that need the global in their own module.
Use eslint-disable-line or eslint-disable-next-line and an inline assignment, like this:
function foo(selector) {
const doc = document; /* eslint-disable-line no-undef */
return doc.querySelector(selector);
}
I have my HTML setup like this:
<script type="module" src="main.js"></script>
and all the ES6 modules work fine. The only problem is I now can't refer to anything from within DevTools (like using the Console and typing in a variable to see it's value or using a function manually).
How do I import modules whilst being able to use the DevTools? Thanks!
One way to make a variable accessable within DevTools is to create it on the window object:
// Variable in your module
var importantNumber = 1;
window.importantNumber = importantNumber;
This method works fine if you just have a couple of variables, but if you need to have access to a lot more variables within DevTools, I would recommend you go to the sources-tab in DevTools, search for your module and adding a breakpoint. When the execution pauses, you have access to all the variables within that module on the DevTools console.
If you want to be able to refer to variables created within the module from the console's global scope, you'll have to deliberately expose each such variable that you want to be visible from the console. Either assign each variable to window (probably not a good idea - the whole point of modules is to make things more modular, without global pollution), or perhaps assign a single object to window, to which you assign module variables. For example:
// in the console:
setTimeout(() => {
window.myModule.foo();
console.log(window.myModule.bar);
});
<script type="module">
function foo() {
console.log('doing foo');
}
const bar = 'a string bar';
const thisModule = { foo, bar };
window.myModule = thisModule;
// If you ever reassign variables, you'll have to reassign them on thisModule too
// or, only reference and reassign properties of thisModule, rather than create independent variables
</script>
For anyone else interested, if you're comfortable with it, use a bundler like Webpack. I don't believe (at least at this point) that the browser will by itself be able to use the DevTools on modules (the other solutions are quite janky, and aren't fantastic to work with).
Hopefully in the future, when all major browsers will be able to support ES6 modules without a bundler, we'll be able to use DevTools.
Using a Helper
I personally use a little helper function in development that allows me to expose a bunch a variables in a single expression. For example, it makes the following two blocks equivalent:
window.playerOne = playerOne;
window.someClass = someClass;
window.localData = localData;
globalize({playerOne, someClass, localData});
The helper looks like this:
const globalize = function(variables) {
Object.entries(variables).forEach(([name, value]) => window[name] = value);
};
I've been seeing a number of people wrapping their controllers with:
function(){
//CODE
}();
What's the benefit / goal there?
That isn't really anything related directly to Angular, it is a JS pattern know as an Immediately Invoked Function Expression.
It is one of the most useful patterns in JavaScript, primarily because of:
Code Encapsulation
Since functions have closures in JS, we can use this pattern to create private data very easily:
var index = (function iife() {
var counter = 0; // <-- this is private, only available inside the closure of inner()
return function inner() {
return counter++;
};
})();
console.log(index()); // 0
console.log(index()); // 1
console.log(index()); // 2
console.log(index.counter) // undefined
We can also pass arguments into an IIFE, which allows us to control how we access the outside context of our IIFE. For example, to make sure $ is actually the jQuery object within your code:
(function ($) {
// here have access to the global jQuery as $ regardless of what window.$ is and
// how many libraries are trying to use $
})(jQuery);
By combining the two ideas above, IIFEs can also be used to implement the module pattern, which is the basis for how RequireJS and NodeJS separate code:
var myModule = (function iife(initData) {
// we can use initData here to initialize our module if necessary
var module = {};
// private vars ...
var privateVar1, privateVar2;
// private methods ...
function privateMethod() {
console.log('yeeey');
}
module.somePublicProperty = 1;
module.somePublicMethod = function() {
privateMethod();
};
return module;
})(/* pass initialization data */);
myModule.somePublicMethod(); // 'yeeey'
The reason you'll find a lot of JavaScript code wrapped in an anonymous function is to isolate it from other code on the page.
The following code will declare a variable called name on a global scope:
var name = "Hello World";
By using that code, any other script on the page attempting to use a variable called name could potentially get an unexpected value of "Hello World" because your script declared it as "Hello World".
By wrapping that code in an anonymous function, you keep the code from conflicting with other variables called name:
(function() {
var name = "Hello World";
})();
In the example above, name is now only available inside of the scope of the anonymous function. It is not global, and therefore cannot conflict with other code on the page.
By wrapping your Angular module in an anonymous function, you prevent your code from conflicting with other code.
Additionally, other people who may use your code won't have to worry about it changing their global scope.
IMHO it's not necessary, even superfluous, as most controllers are already functions:
'use strict';
angular.module('MyApp').controller('AboutController', ['$scope'
function ($scope) {
$scope.title = 'About Us';
}
]);
Everything #nem035 and #tcasey said is correct but it also have another side effect.
If you use tools like Grunt or Gulp it also allow you to have working dists to put in production.
If you do not use Immediate Invoke Pattern you'll most likely have minification problems like:
State X is already defined!
Unknown provider
. . .
I suggest you to wrap all your js modules with this pattern.
I hope I've been helpful.
There are many benefits of you immediately-invoked-function-expression short IIFE and it's best practice to use it. This way each of angular service or controller become isolate and you don't run into global variables as it can happened if you don't use IIFE.
read more about it
Thus far I've worked only with relatively small projects (and mostly alone), but this time I have to collaborate with other programmers... basically because of that I must plan the structure of the website very carefully for the avoidance of spending hours debugging the code.
At this point I suppose doing that in the following manner. I divide my code in modules and store each module in a separate file inside an object (or a function) with a made-up name (lzheA, lzheB, lzheC etc.) to avoid conflicts whether an object with the same name was used in an another piece of code. When the document is loaded, I declare a variable (an object) that I use as a main namespace of the application. Properties of the object are the modules I defined before.
// file BI.lib.js
var lzheA = {
foo: function() {
},
bar: function() {
},
}
// file BI.init.js
function lzheK() {
BI.loadPage();
}
// file BI.loadPage.js
function lzheC() {
var result = document.getElementById('result');
result.innerHTML = "that worked";
}
// and so on
var lzheA,lzheB,lzheD,lzheE,lzheF,lzheG,lzheH,lzheI,lzheJ;
// doing the following when the document is loaded
var BI = {
lib: lzheA,
menu: lzheB,
loadPage: lzheC,
customScripts: lzheD,
_index: lzheE,
_briefs: lzheF,
_shop: lzheG,
_cases: lzheH,
_blog: lzheI,
_contacts: lzheJ,
init: lzheK,
}
BI.init();
https://jsfiddle.net/vwc2og57/2/
The question... is this way of structuring worth living or did I miss something because of lack of experience? Would the made-up names of the modules confuse you regardless of the fact that each one used only twice - while declaring the variable and assigning it to a property?
I consider the namespaces a good option when you want to modularize applications in Javascript. But I declare them in a different way
var myModule = myModule || {}; // This will allow to use the module in other places, declaring more than one specificComponent in other js file for example
myModule.specificComponent = (function(){
// Private things
var myVar = {};
var init = function() {
// Code
};
return {
init: init // Public Stuff
};
})();
If you want to call the init method, you would call it like this
myModule.specificComponent.init();
With this approach, i guarantee that the module will not be overwritten by another declaration in another place, and also I can declare internal components into my namespaces.
Also, the trick of just exposing what you want inside the return block, will make your component safer and you will be encapsulating your code in a pretty way.
Hope it helps
I try to submit a simple Firefox add-on and got a message from AMO editor about wrapping of variables and functions within a JavaScript object in order to prevent conflicts with other add-ons that may be installed by users. The working code is very simple and looks:
function analyze() {
var uri = document.getElementById('urlbar').value;
var requrl="http://www.myanalyzingsiteaddress.com/" + (uri);
gBrowser.selectedTab = gBrowser.addTab(requrl);
}
Is it enough to make other var names to avoid eventual conflicts or could you point me to other code change, which would fulfill the AMO editor's instruction?
Thank you in advance!
Evgenij
You should have been pointed to Javascript Object Management from the XUL School tutorial.
analyze is a generic name. In an overlay there is only one scope/namespace which is shared by the browser code itself and any additional extension code. It is therefore possible that either the browser or another add-on uses analyze as well and boom.
You need to avoid that by making names a specifc as possible. E.g.
function my_addon_id_analyze() ...
Use an an object with a (pseudo) unique name.
if (!("org" in this)) {
this.org = {};
}
if (!("example" in org)) {
org.example = {};
}
org.example.addonid = {
analyze: function() ...
};
// call
org.example.addonid.analyze();
Or even "hide" your code in an anonymous function. This is then hidden from the DOM as well, so no more <button id="example.org.addonid.mybutton" onclick="analyze()"> event handling. But you can always use addEventListener.
(function() {
"use strict";
function analyze() ...
// wire up an event handler instead of onlick
document.getElementById("example.org.addonid.mybutton").addEventListener("click", analyze);
})();
Mix some/all of the above, as long as you avoid short/generic names:
if (!("org" in this)) {
this.org = {};
}
if (!("example" in org)) {
org.example = {};
}
org.example.addonid = (function() {
function analyze() ...
function notvisibleoutside() ...
// return object of "exported"/"visible" functions.
return {
analyze: analyze
};
})();
// call
org.example.addonid.analyze()
Also, keep in mind that missing var (or let/const) declarations will implicitly declare the variable in the global scope.
E.g.:
function abc() {
for (i = 0; i < 10; ++i) doSomething();
}
Will implicitly declare a variable named i in the global scope (in a XUL window the global scope is window, therefore this will create window.i). Implicit declarations may therefore not only cause conflicts but also create quasi memory leaks, e.g.
function call_me_to_leak_1MB() {
hugeArray = new ArrayBuffer(1<<20);
}
will declare window.hugeArray that lives as long as the browser window is open instead of using a local variable that gets garbage collected as soon as the variables goes out of scope (and there are no more other references, of course).
Using strict mode makes implicit declaration an error, which is helpful to catch and avoid such mistakes early.
So much for the Javascript part. There is still other stuff that might clash.
DOM Ids: Use unique <button id="example.org.addonid.mybutton">, or more CSS friendly <button id="example-org-addonid-mybutton"> (or at the very least something like addonid-mybutton) instead of <button id="mybutton">
CSS: Never style random elements.
No: button { color: green; }
Yep: #example-org-addonid-mybutton { color: green; }
chrome.manifest:
No: content generic content/
Yep: content example-org-addonid content/.