|
1 | 1 |
|
2 |
| - |
3 | 2 | # Global object
|
4 | 3 |
|
5 |
| -When JavaScript was created, there was an idea of a "global object" that provides all global variables and functions. It was planned that multiple in-browser scripts would use that single global object and share variables through it. |
6 |
| - |
7 |
| -Since then, JavaScript greatly evolved, and that idea of linking code through global variables became much less appealing. In modern JavaScript, the concept of modules took its place. |
8 |
| - |
9 |
| -But the global object still remains in the specification. |
| 4 | +The global object provides variables and functions that are available anywhere. Mostly, the ones that are built into the language or the host environment. |
10 | 5 |
|
11 | 6 | In a browser it is named "window", for Node.JS it is "global", for other environments it may have another name.
|
12 | 7 |
|
13 |
| -It does two things: |
| 8 | +For instance, we can call `alert` as a method of `window`: |
14 | 9 |
|
15 |
| -1. Provides access to built-in functions and values, defined by the specification and the environment. |
16 |
| - For instance, we can call `alert` directly or as a method of `window`: |
| 10 | +```js run |
| 11 | +alert("Hello"); |
17 | 12 |
|
18 |
| - ```js run |
19 |
| - alert("Hello"); |
20 |
| - |
21 |
| - // the same as |
22 |
| - window.alert("Hello"); |
23 |
| - ``` |
| 13 | +// the same as |
| 14 | +window.alert("Hello"); |
| 15 | +``` |
24 | 16 |
|
25 |
| - The same applies to other built-ins. E.g. we can use `window.Array` instead of `Array`. |
| 17 | +We can reference other built-in functions like `Array` as `window.Array` and create our own properties on it. |
26 | 18 |
|
27 |
| -2. Provides access to global Function Declarations and `var` variables. We can read and write them using its properties, for instance: |
| 19 | +## Browser: the "window" object |
28 | 20 |
|
29 |
| - <!-- no-strict to move variables out of eval --> |
30 |
| - ```js untrusted run no-strict refresh |
31 |
| - var phrase = "Hello"; |
| 21 | +For historical reasons, in-browser `window` object is a bit messed up. |
32 | 22 |
|
33 |
| - function sayHi() { |
34 |
| - alert(phrase); |
35 |
| - } |
| 23 | +1. It provides the "browser window" functionality, besides playing the role of a global object. |
36 | 24 |
|
37 |
| - // can read from window |
38 |
| - alert( window.phrase ); // Hello (global var) |
39 |
| - alert( window.sayHi ); // function (global function declaration) |
| 25 | + We can use `window` to access properties and methods, specific to the browser window: |
40 | 26 |
|
41 |
| - // can write to window (creates a new global variable) |
42 |
| - window.test = 5; |
| 27 | + ```js run |
| 28 | + alert(window.innerHeight); // shows the browser window height |
43 | 29 |
|
44 |
| - alert(test); // 5 |
| 30 | + window.open('http://google.com'); // opens a new browser window |
45 | 31 | ```
|
46 | 32 |
|
47 |
| -...But the global object does not have variables declared with `let/const`! |
| 33 | +2. Top-level `var` variables and function declarations automatically become properties of `window`. |
48 | 34 |
|
49 |
| -```js untrusted run no-strict refresh |
50 |
| -*!*let*/!* user = "John"; |
51 |
| -alert(user); // John |
| 35 | + For instance: |
| 36 | + ```js untrusted run no-strict refresh |
| 37 | + var x = 5; |
52 | 38 |
|
53 |
| -alert(window.user); // undefined, don't have let |
54 |
| -alert("user" in window); // false |
55 |
| -``` |
| 39 | + alert(window.x); // 5 (var x becomes a property of window) |
56 | 40 |
|
57 |
| -```smart header="The global object is not a global Environment Record" |
58 |
| -In versions of ECMAScript prior to ES-2015, there were no `let/const` variables, only `var`. And global object was used as a global Environment Record (wordings were a bit different, but that's the gist). |
| 41 | + window.x = 0; |
59 | 42 |
|
60 |
| -But starting from ES-2015, these entities are split apart. There's a global Lexical Environment with its Environment Record. And there's a global object that provides *some* of the global variables. |
| 43 | + alert(x); // 0, variable modified |
| 44 | + ``` |
61 | 45 |
|
62 |
| -As a practical difference, global `let/const` variables are definitively properties of the global Environment Record, but they do not exist in the global object. |
| 46 | + Please note, that doesn't happen with more modern `let/const` declarations: |
63 | 47 |
|
64 |
| -Naturally, that's because the idea of a global object as a way to access "all global things" comes from ancient times. Nowadays it's not considered to be a good thing. Modern language features like `let/const` do not make friends with it, but old ones are still compatible. |
65 |
| -``` |
| 48 | + ```js untrusted run no-strict refresh |
| 49 | + let x = 5; |
66 | 50 |
|
67 |
| -## Uses of "window" |
| 51 | + alert(window.x); // undefined ("let" doesn't create a window property) |
| 52 | + ``` |
68 | 53 |
|
69 |
| -In server-side environments like Node.JS, the `global` object is used exceptionally rarely. Probably it would be fair to say "never". |
| 54 | +3. Also, all scripts share the same global scope, so variables declared in one `<script>` become visible in another ones: |
70 | 55 |
|
71 |
| -In-browser `window` is sometimes used though. |
| 56 | + ```html run |
| 57 | + <script> |
| 58 | + var a = 1; |
| 59 | + let b = 2; |
| 60 | + </script> |
72 | 61 |
|
73 |
| -Usually, it's not a good idea to use it, but here are some examples you can meet. |
| 62 | + <script> |
| 63 | + alert(a); // 1 |
| 64 | + alert(b); // 2 |
| 65 | + </script> |
| 66 | + ``` |
74 | 67 |
|
75 |
| -1. To access exactly the global variable if the function has the local one with the same name. |
| 68 | +4. And, a minor thing, but still: the value of `this` in the global scope is `window`. |
76 | 69 |
|
77 | 70 | ```js untrusted run no-strict refresh
|
78 |
| - var user = "Global"; |
79 |
| - |
80 |
| - function sayHi() { |
81 |
| - var user = "Local"; |
82 |
| - |
83 |
| - *!* |
84 |
| - alert(window.user); // Global |
85 |
| - */!* |
86 |
| - } |
87 |
| - |
88 |
| - sayHi(); |
| 71 | + alert(this); // window |
89 | 72 | ```
|
90 | 73 |
|
91 |
| - Such use is a workaround. Would be better to name variables differently, that won't require use to write the code it this way. And please note `"var"` before `user`. The trick doesn't work with `let` variables. |
| 74 | +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. |
92 | 75 |
|
93 |
| -2. To check if a certain global variable or a builtin exists. |
| 76 | +Is it good that different scripts (possibly from different sources) see variables of each other? |
94 | 77 |
|
95 |
| - For instance, we want to check whether a global function `XMLHttpRequest` exists. |
| 78 | +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. |
96 | 79 |
|
97 |
| - We can't write `if (XMLHttpRequest)`, because if there's no `XMLHttpRequest`, there will be an error (variable not defined). |
| 80 | +As of now, the multi-purpose `window` is considered a design mistake in the language. |
98 | 81 |
|
99 |
| - But we can read it from `window.XMLHttpRequest`: |
| 82 | +Luckily, there's a "road out of hell", called "Javascript modules". |
100 | 83 |
|
101 |
| - ```js run |
102 |
| - if (window.XMLHttpRequest) { |
103 |
| - alert('XMLHttpRequest exists!') |
104 |
| - } |
105 |
| - ``` |
| 84 | +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`. |
106 | 85 |
|
107 |
| - If there is no such global function then `window.XMLHttpRequest` is just a non-existing object property. That's `undefined`, no error, so it works. |
| 86 | +- In a module, `var x` does not become a property of `window`: |
108 | 87 |
|
109 |
| - We can also write the test without `window`: |
| 88 | + ```html run |
| 89 | + <script type="module"> |
| 90 | + var x = 5; |
110 | 91 |
|
111 |
| - ```js |
112 |
| - if (typeof XMLHttpRequest == 'function') { |
113 |
| - /* is there a function XMLHttpRequest? */ |
114 |
| - } |
| 92 | + alert(window.x); // undefined |
| 93 | + </script> |
115 | 94 | ```
|
116 | 95 |
|
117 |
| - This doesn't use `window`, but is (theoretically) less reliable, because `typeof` may use a local XMLHttpRequest, and we want the global one. |
| 96 | +- Two modules that do not see variables of each other: |
118 | 97 |
|
| 98 | + ```html run |
| 99 | + <script type="module"> |
| 100 | + let x = 5; |
| 101 | + </script> |
119 | 102 |
|
120 |
| -3. To take the variable from the right window. That's probably the most valid use case. |
| 103 | + <script type="module"> |
| 104 | + alert(window.x); // undefined |
| 105 | + alert(x); // Error: undeclared variable |
| 106 | + </script> |
| 107 | + ``` |
121 | 108 |
|
122 |
| - A browser may open multiple windows and tabs. A window may also embed another one in `<iframe>`. Every browser window has its own `window` object and global variables. JavaScript allows windows that come from the same site (same protocol, host, port) to access variables from each other. |
| 109 | +- And, the last minor thing, the top-level value of `this` in a module is `undefined` (why should it be `window` anyway?): |
123 | 110 |
|
124 |
| - That use is a little bit beyond our scope for now, but it looks like: |
125 | 111 | ```html run
|
126 |
| - <iframe src="/" id="iframe"></iframe> |
127 |
| -
|
128 |
| - <script> |
129 |
| - alert( innerWidth ); // get innerWidth property of the current window (browser only) |
130 |
| - alert( Array ); // get Array of the current window (javascript core builtin) |
131 |
| -
|
132 |
| - // when the iframe loads... |
133 |
| - iframe.onload = function() { |
134 |
| - // get width of the iframe window |
135 |
| - *!* |
136 |
| - alert( iframe.contentWindow.innerWidth ); |
137 |
| - */!* |
138 |
| - // get the builtin Array from the iframe window |
139 |
| - *!* |
140 |
| - alert( iframe.contentWindow.Array ); |
141 |
| - */!* |
142 |
| - }; |
| 112 | + <script type="module"> |
| 113 | + alert(this); // undefined |
143 | 114 | </script>
|
144 | 115 | ```
|
145 | 116 |
|
146 |
| - Here, first two alerts use the current window, and the latter two take variables from `iframe` window. Can be any variables if `iframe` originates from the same protocol/host/port. |
| 117 | +**Using `<script type="module">` fixes the design flaw of the language by separating top-level scope from `window`.** |
| 118 | + |
| 119 | +We'll cover more features of modules later, in the chapter [](info:modules). |
147 | 120 |
|
148 |
| -## "this" and global object |
| 121 | +## Valid uses of the global object |
149 | 122 |
|
150 |
| -Sometimes, the value of `this` is exactly the global object. That's rarely used, but some scripts rely on that. |
| 123 | +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). |
151 | 124 |
|
152 |
| -1. In the browser, the value of `this` in the global area is `window`: |
| 125 | + Here we put the information about the current user into a global object, to be accessible from all other scripts: |
153 | 126 |
|
154 | 127 | ```js run
|
155 |
| - // outside of functions |
156 |
| - alert( this === window ); // true |
| 128 | + // explicitly assign it to `window` |
| 129 | + window.currentUser = { |
| 130 | + name: "John", |
| 131 | + age: 30 |
| 132 | + }; |
| 133 | + |
| 134 | + // then, elsewhere, in another script |
| 135 | + alert(window.currentUser.name); // John |
157 | 136 | ```
|
158 | 137 |
|
159 |
| - Other, non-browser environments, may use another value for `this` in such cases. |
| 138 | +2. We can test the global object for support of modern language features. |
160 | 139 |
|
161 |
| -2. When a function with `this` is called in non-strict mode, it gets the global object as `this`: |
162 |
| - ```js run no-strict |
163 |
| - // not in strict mode (!) |
164 |
| - function f() { |
165 |
| - alert(this); // [object Window] |
| 140 | + For instance, test if a build-in `Promise` object exists (it doesn't in really old browsers): |
| 141 | + ```js run |
| 142 | + if (!window.Promise) { |
| 143 | + alert("Your browser is really old!"); |
166 | 144 | }
|
| 145 | + ``` |
| 146 | +
|
| 147 | +3. We can create "polyfills": add functions that are not supported by the environment (say, an old browser), but exist in the modern standard. |
167 | 148 |
|
168 |
| - f(); // called without an object |
| 149 | + ```js run |
| 150 | + if (!window.Promise) { |
| 151 | + window.Promise = ... // custom implementation of the modern language feature |
| 152 | + } |
169 | 153 | ```
|
170 | 154 |
|
171 |
| - By specification, `this` in this case must be the global object, even in non-browser environments like Node.JS. That's for compatibility with old scripts, in strict mode `this` would be `undefined`. |
| 155 | +...And of course, if we're in a browser, using `window` to access browser window features (not as a global object) is completely fine. |
0 commit comments