rewrite global object

This commit is contained in:
Ilya Kantor 2019-06-14 17:52:17 +03:00
parent 8be8d6cf17
commit 76d599fda3

View file

@ -1,11 +1,13 @@
# Global object # Global object
The global object provides variables and functions that are available anywhere. Mostly, the ones that are built into the language or the host environment. The global object provides variables and functions that are available anywhere. Mostly, the ones that are built into the language or the environment.
In a browser it is named "window", for Node.js it is "global", for other environments it may have another name. In a browser it is named `window`, for Node.js it is `global`, for other environments it may have another name.
For instance, we can call `alert` as a method of `window`: Recently, `globalThis` was added to the language, as a standartized name for a global object, that should be supported across all environments. In some browsers, namely non-Chromium Edge, `globalThis` is not yet supported, but can be easily polyfilled.
All properties of the global object can be accessed directly:
```js run ```js run
alert("Hello"); alert("Hello");
@ -14,128 +16,47 @@ alert("Hello");
window.alert("Hello"); window.alert("Hello");
``` ```
We can reference other built-in functions like `Array` as `window.Array` and create our own properties on it. In a browser, global variables declared with `var` become the property of the global object:
## Browser: the "window" object ```js run untrusted refresh
var gVar = 5;
For historical reasons, in-browser `window` object is a bit messed up. alert(window.gVar); // 5 (became a property of the global object)
```
1. It provides the "browser window" functionality, besides playing the role of a global object. Please don't rely on that! This behavior exists for compatibility reasons. Modern scripts use JavaScript modules where such thing doesn't happen. We'll cover them later in the chapter [](info:modules).
We can use `window` to access properties and methods, specific to the browser window: Also, more modern variable declarations `let` and `const` do not exhibit such behavior at all:
```js run untrusted refresh
let gLet = 5;
alert(window.gLet); // undefined (doesn't become a property of the global object)
```
If a value is so important that you'd like to make it available globally, write it directly as a property:
```js run ```js run
alert(window.innerHeight); // shows the browser window height *!*
// make current user information global, to let all scripts access it
window.open('http://google.com'); // opens a new browser window
```
2. Top-level `var` variables and function declarations automatically become properties of `window`.
For instance:
```js untrusted run no-strict refresh
var x = 5;
alert(window.x); // 5 (var x becomes a property of window)
window.x = 0;
alert(x); // 0, variable modified
```
Please note, that doesn't happen with more modern `let/const` declarations:
```js untrusted run no-strict refresh
let x = 5;
alert(window.x); // undefined ("let" doesn't create a window property)
```
3. Also, all scripts share the same global scope, so variables declared in one `<script>` become visible in another ones:
```html run
<script>
var a = 1;
let b = 2;
</script>
<script>
alert(a); // 1
alert(b); // 2
</script>
```
4. And, a minor thing, but still: the value of `this` in the global scope is `window`.
```js untrusted run no-strict refresh
alert(this); // window
```
Why was it made like this? At the time of the language creation, the idea to merge multiple aspects into a single `window` object was to "make things simple". But since then many things changed. Tiny scripts became big applications that require proper architecture.
Is it good that different scripts (possibly from different sources) see variables of each other?
No, it's not, because it may lead to naming conflicts: the same variable name can be used in two scripts for different purposes, so they will conflict with each other.
As of now, the multi-purpose `window` is considered a design mistake in the language.
Luckily, there's a "road out of hell", called "JavaScript modules".
If we set `type="module"` attribute on a `<script>` tag, then such script is considered a separate "module" with its own top-level scope (lexical environment), not interfering with `window`.
- In a module, `var x` does not become a property of `window`:
```html run
<script type="module">
var x = 5;
alert(window.x); // undefined
</script>
```
- Two modules that do not see variables of each other:
```html run
<script type="module">
let x = 5;
</script>
<script type="module">
alert(window.x); // undefined
alert(x); // Error: undeclared variable
</script>
```
- And, the last minor thing, the top-level value of `this` in a module is `undefined` (why should it be `window` anyway?):
```html run
<script type="module">
alert(this); // undefined
</script>
```
**Using `<script type="module">` fixes the design flaw of the language by separating top-level scope from `window`.**
We'll cover more features of modules later, in the chapter [](info:modules).
## Valid uses of the global object
1. Using global variables is generally discouraged. There should be as few global variables as possible, but if we need to make something globally visible, we may want to put it into `window` (or `global` in Node.js).
Here we put the information about the current user into a global object, to be accessible from all other scripts:
```js run
// explicitly assign it to `window`
window.currentUser = { window.currentUser = {
name: "John", name: "John"
age: 30
}; };
*/!*
// then, elsewhere, in another script // somewhere else in code
alert(currentUser.name); // John
// or, if we have a local variable with the name "value"
// get it from window explicitly (safe!)
alert(window.currentUser.name); // John alert(window.currentUser.name); // John
``` ```
2. We can test the global object for support of modern language features. That said, using global variables is generally discouraged. There should be as few global variables as possible. The code design where a function gets "input" variables and produces certain "outcome" is clearer, less prone to errors and easier to test.
## Using for polyfills
We can test the global object for support of modern language features.
For instance, test if a build-in `Promise` object exists (it doesn't in really old browsers): For instance, test if a build-in `Promise` object exists (it doesn't in really old browsers):
```js run ```js run
@ -144,7 +65,7 @@ We'll cover more features of modules later, in the chapter [](info:modules).
} }
``` ```
3. We can create "polyfills": add functions that are not supported by the environment (say, an old browser), but exist in the modern standard. If there's none (say, we're in an old browser), we can create "polyfills": add functions that are not supported by the environment, but exist in the modern standard.
```js run ```js run
if (!window.Promise) { if (!window.Promise) {
@ -152,4 +73,15 @@ We'll cover more features of modules later, in the chapter [](info:modules).
} }
``` ```
...And of course, if we're in a browser, using `window` to access browser window features (not as a global object) is completely fine. ## Summary
- The global object holds variables that should be available everywhere.
That includes JavaScript built-ins, such as `Array` and environment-specific values, such as `window.innerHeight` -- the window height in the browser.
- The global object has a universal name `globalThis`.
...But more often is referred by "old-school" environment-specific names, such as `window` (browser) and `global` (Node.js). As `globalThis` is a recent proposal, it's not supported in non-Chromium Edge (but can be polyfilled).
- We should store values in the global object only if they're truly global for our project. And keep their number at minimum.
- In-browser, unless we're using [modules](info:modules), a global variable declared with `var` becomes a property of the global object.
To make the code easier to understand and more future-proof, we should operate directly on the properties of the global object: `window.x = ...` instead of `var x = ...`.