rewrite global object
This commit is contained in:
parent
8be8d6cf17
commit
76d599fda3
1 changed files with 53 additions and 121 deletions
|
@ -1,11 +1,13 @@
|
|||
|
||||
# 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
|
||||
alert("Hello");
|
||||
|
@ -14,142 +16,72 @@ 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
|
||||
alert(window.innerHeight); // shows the browser window height
|
||||
```js run untrusted refresh
|
||||
let gLet = 5;
|
||||
|
||||
window.open('http://google.com'); // opens a new browser window
|
||||
```
|
||||
alert(window.gLet); // undefined (doesn't become a property of the global object)
|
||||
```
|
||||
|
||||
2. Top-level `var` variables and function declarations automatically become properties of `window`.
|
||||
If a value is so important that you'd like to make it available globally, write it directly as a property:
|
||||
|
||||
For instance:
|
||||
```js untrusted run no-strict refresh
|
||||
var x = 5;
|
||||
```js run
|
||||
*!*
|
||||
// make current user information global, to let all scripts access it
|
||||
window.currentUser = {
|
||||
name: "John"
|
||||
};
|
||||
*/!*
|
||||
|
||||
alert(window.x); // 5 (var x becomes a property of window)
|
||||
// somewhere else in code
|
||||
alert(currentUser.name); // John
|
||||
|
||||
window.x = 0;
|
||||
// or, if we have a local variable with the name "value"
|
||||
// get it from window explicitly (safe!)
|
||||
alert(window.currentUser.name); // John
|
||||
```
|
||||
|
||||
alert(x); // 0, variable modified
|
||||
```
|
||||
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.
|
||||
|
||||
Please note, that doesn't happen with more modern `let/const` declarations:
|
||||
## Using for polyfills
|
||||
|
||||
```js untrusted run no-strict refresh
|
||||
let x = 5;
|
||||
We can test the global object for support of modern language features.
|
||||
|
||||
alert(window.x); // undefined ("let" doesn't create a window property)
|
||||
```
|
||||
For instance, test if a build-in `Promise` object exists (it doesn't in really old browsers):
|
||||
```js run
|
||||
if (!window.Promise) {
|
||||
alert("Your browser is really old!");
|
||||
}
|
||||
```
|
||||
|
||||
3. Also, all scripts share the same global scope, so variables declared in one `<script>` become visible in another ones:
|
||||
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.
|
||||
|
||||
```html run
|
||||
<script>
|
||||
var a = 1;
|
||||
let b = 2;
|
||||
</script>
|
||||
```js run
|
||||
if (!window.Promise) {
|
||||
window.Promise = ... // custom implementation of the modern language feature
|
||||
}
|
||||
```
|
||||
|
||||
<script>
|
||||
alert(a); // 1
|
||||
alert(b); // 2
|
||||
</script>
|
||||
```
|
||||
## Summary
|
||||
|
||||
4. And, a minor thing, but still: the value of `this` in the global scope is `window`.
|
||||
- The global object holds variables that should be available everywhere.
|
||||
|
||||
```js untrusted run no-strict refresh
|
||||
alert(this); // window
|
||||
```
|
||||
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`.
|
||||
|
||||
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.
|
||||
...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.
|
||||
|
||||
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 = {
|
||||
name: "John",
|
||||
age: 30
|
||||
};
|
||||
|
||||
// then, elsewhere, in another script
|
||||
alert(window.currentUser.name); // John
|
||||
```
|
||||
|
||||
2. 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):
|
||||
```js run
|
||||
if (!window.Promise) {
|
||||
alert("Your browser is really old!");
|
||||
}
|
||||
```
|
||||
|
||||
3. We can create "polyfills": add functions that are not supported by the environment (say, an old browser), but exist in the modern standard.
|
||||
|
||||
```js run
|
||||
if (!window.Promise) {
|
||||
window.Promise = ... // custom implementation of the modern language feature
|
||||
}
|
||||
```
|
||||
|
||||
...And of course, if we're in a browser, using `window` to access browser window features (not as a global object) is completely fine.
|
||||
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 = ...`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue