Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid accidentally implicitly referring to properties on the global object?

Is it possible to execute a block of code without the implicit with(global) context that all scripts seem to have by default? For example, in a browser, would there be any way to set up a script so that a line such as

const foo = location;

throws

Uncaught ReferenceError: location is not defined

instead of accessing window.location, when location has not been declared first? Lacking that, is there a way that such an implicit reference could result in a warning of some sort? It can be a source of bugs when writing code (see below), so having a way to guard against it could be useful.

(Of course, due to ordinary scoping rules, it's possible to declare another variable with the same name using const or let, or within an inner block, to ensure that using that variable name references the new variable rather than the global property, but that's not the same thing.)

This may be similar to asking whether it's possible to stop referencing a property from within an actual with statement:

const obj = { prop: 'prop' };
with (obj) {
  // how to make referencing "prop" from somewhere within this block throw a ReferenceError
}

It's known that with should not be used in the first place, but unfortunately it seems we have no choice when it comes to the with(global), which occasionally saves a few characters at the expense of confusing bugs which pop up somewhat frequently: 1 2 3 4 5 6. For example:

var status = false;
if (status) {
  console.log('status is actually truthy!');
}

(the issue here: window.status is a reserved property - when assigned to, it coerces the assigned expression to a string)

These sorts of bugs are the same reason that explicit use of with is discouraged or prohibited, yet the implicit with(global) continues to cause issues, even in strict mode, so figuring out a way around it would be useful.

like image 604
CertainPerformance Avatar asked Feb 19 '19 23:02

CertainPerformance


People also ask

What should be used to prevent creating global variables accidentally?

Also, in a language like JavaScript, you could end up creating a global variable accidentally by forgetting var . You can then unknowingly start using a global variable when the intention was to use a local variable. A workaround to avoid such conflicts is by using prefixes on global variable names.

What is the name of the object to access global variables from inside a script?

A global object is an object that always exists in the global scope. In JavaScript, there's always a global object defined. In a web browser, when scripts create global variables defined with the var keyword, they're created as members of the global object.


1 Answers

There are some things you need to consider before trying to answer this question.

For example, take the Object constructor. It is a "Standard built-in object".

window.status is part of the Window interface.

Obviously, you don't want status to refer to window.status, but do you want Object to refer to window.Object?


The solution to your problem of it not being able to be redefined is to use a IIFE, or a module, which should be what you are doing anyways.

(() => {
  var status = false;
  if (!status) {
    console.log('status is now false.');
  }
})();

And to prevent accidentally using global variables, I would just set up your linter to warn against it. Forcing it using a solution like with (fake_global) would not only have errors exclusively at run time, which might be not caught, but also be slower.


Specifically with ESLint, I can't seem to find a "good" solution. Enabling browser globals allows implicit reads.

I would suggest no-implicit-globals (As you shouldn't be polluting the global scope anyways, and it prevents the var status not defining anything problem), and also not enabling all browser globals, only, say, window, document, console, setInterval, etc., like you said in the comments.

Look at the ESLint environments to see which ones you would like to enable. By default, things like Object and Array are in the global scope, but things like those listed above and atob are not.

To see the exact list of globals, they are defined by this file in ESLint and the globals NPM package. I would would pick from (a combination of) "es6", "worker" or "shared-node-browser".

The eslintrc file would have:

{
    "rules": {
        "no-implicit-globals": "error"
    },
    "globals": {
        "window": "readonly",
        "document": "readonly"
    },
    "env": {
        "browser": false,
        "es6": [true/false],
        "worker": [true/false],
        "shared-node-browser": [true/false]
    }
}
like image 139
Artyer Avatar answered Oct 17 '22 11:10

Artyer