Can you locally declare a global variable for eslint? - javascript

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);
}

Related

OnChange function not defined when I reference my script as a module? [duplicate]

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);
};

use `module` as namespace

I've been using a pattern in my node.js modules that seems so obvious to me that I assume there must be something wrong with it or I would see more people doing it. To keep private variables that are global to the module, I simply attach them as properties on the module object. Like so:
module.exports = {
init: function() {
module.someClient = initializeSomethingHere()
},
someMethod: function(done) {
module.someClient.doSomething(done)
}
}
This seems preferable to me than something like this...
var someClient;
module.exports = {
init: function() {
someClient = initializeSomethingHere()
},
someMethod: function(done) {
someClient.doSomething(done)
}
}
...because in the second example you need to go searching for var someClient at the top of the file to make sure that the omission of the var keyword is intentional within the init method. I've never seen this pattern used elsewhere though, so I wonder if I'm missing something that makes it less than ideal.
Thoughts?
There are a couple of possible downsides that come to mind.
1) It's technically possible for those properties to be accessed and modified outside the module, but only if a reference to the module itself is available outside it. Something that makes the module available through its own exports (module.exports = module; being the simplest example) would expose them.
2) You could have a naming conflict with a builtin property or a future builtin property that doesn't exist yet (which would cause your code to break in future versions of node.js). This could be very problematic and very hard to debug. Currently the built-in properties on a module object are: children, exports, filename, id, loaded, paths, and parent.
because in the second example you need to go searching for var someClient at the top of the file to make sure that the omission of the var keyword is intentional within the init method.
If that is the reason, you could just use a namespace that isn't module. For instance by adding var private = {}; to the top of each file and then using private.someClient instead of module.someClient.
Also 'use strict'; so that the accidental omission of var is an error and not an accidental global.
Drawbacks
Option 1: This practice is prone to naming conflict with a builtin property or a future builtin property that doesn't exist yet, as #paulpro stated in his answer.
Option 2: If you miss var keyword and expose the someClient globally. Besides you need to go searching for var someClient at the top of the file all the time.
Alternative
As JavaScript has function scope it's better to define everything within a function. Creating private and public members within a function. Below is a design pattern which one can follow:
'use strict';
module.exports = (function(){
//private
var someClient;
//public properties
var that={};
that.init = function() {
someClient = initializeSomethingHere()
},
that.someMethod: function(done) {
someClient.doSomething(done)
}
//expose only the required methods
return that;
})();
Here only exposed methods are those which are attached to that object. And all rest are private to the function scope. Even if you miss the var keyword in someClient it won't be accessible out side of the function, which won't ever happen if you use 'use strict' at the top.
Logic is first time when you require the xyz.js file it will return the that object rather than the function.

How to make a variable global from inside a function [duplicate]

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.

Why global "this.function" is not accessible without "this."?

this.func0 = function() {
console.log('hi')
}
this.func0() // this works
func0() // but not this?
Why func0() is not working?
Aren't this.f() and just f() both supposed to access global object?
The important thing to understand here is, in Node.js, each and every JavaScript file will be treated as a separate module.
So, within the JavaScript file, this will refer to the current module object, not the global object. We have something called global object in Node.js, which is somewhat similar to browsers' window object.
This is what you are doing with your program
this.func0 = function() {
console.log('hi')
}
you are creating a function object and assigning it to the current module's func0 attribute.
this.func0();
you are calling current module's func0.
func0();
JavaScript, searches for func0 in the current scope, then in the global scope but it doesn't find it anywhere. That is why it fails with the ReferenceError
ReferenceError: func0 is not defined
If you really wanted to set that in the global scope, then you should do it like this
global.func0 = function() {
console.log('hi')
}
func0();
// hi

How to wrap loose variables and functions within a JavaScript object

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/.

Categories