Skip to content

Commit 2ee2751

Browse files
committed
refactor promise, geneerators, global object
1 parent be9c5a7 commit 2ee2751

File tree

69 files changed

+881
-624
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+881
-624
lines changed

1-js/04-object-basics/04-object-methods/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ sayHi(); // undefined
214214

215215
In this case `this` is `undefined` in strict mode. If we try to access `this.name`, there will be an error.
216216

217-
In non-strict mode (if one forgets `use strict`) the value of `this` in such case will be the *global object* (`window` in a browser, we'll get to it later). This is a historical behavior that `"use strict"` fixes.
217+
In non-strict mode the value of `this` in such case will be the *global object* (`window` in a browser, we'll get to it later in the chapter [](info:global-object)). This is a historical behavior that `"use strict"` fixes.
218218

219219
Please note that usually a call of a function that uses `this` without an object is not normal, but rather a programming mistake. If a function has `this`, then it is usually meant to be called in the context of an object.
220220

1-js/06-advanced-functions/03-closure/article.md

Lines changed: 96 additions & 78 deletions
Large diffs are not rendered by default.

1-js/06-advanced-functions/04-var/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,4 @@ There are two main differences of `var`:
182182

183183
There's one more minor difference related to the global object, we'll cover that in the next chapter.
184184

185-
These differences are actually a bad thing most of the time. First, we can't create block-local variables. And hoisting just creates more space for errors. So, for new scripts `var` is used exceptionally rarely.
185+
These differences are actually a bad thing most of the time. Block-level variables is such a great thing. That's why `let` was introduced in the standard long ago, and is now a major way (along with `const`) to declare a variable.
Lines changed: 93 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,155 @@
11

2-
32
# Global object
43

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

116
In a browser it is named "window", for Node.JS it is "global", for other environments it may have another name.
127

13-
It does two things:
8+
For instance, we can call `alert` as a method of `window`:
149

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");
1712

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+
```
2416

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

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
2820

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

33-
function sayHi() {
34-
alert(phrase);
35-
}
23+
1. It provides the "browser window" functionality, besides playing the role of a global object.
3624

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:
4026

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
4329

44-
alert(test); // 5
30+
window.open('http://google.com'); // opens a new browser window
4531
```
4632

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`.
4834

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;
5238

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)
5640

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;
5942

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+
```
6145

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:
6347

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;
6650

67-
## Uses of "window"
51+
alert(window.x); // undefined ("let" doesn't create a window property)
52+
```
6853

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:
7055

71-
In-browser `window` is sometimes used though.
56+
```html run
57+
<script>
58+
var a = 1;
59+
let b = 2;
60+
</script>
7261

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+
```
7467

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`.
7669

7770
```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
8972
```
9073

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

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?
9477

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

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

99-
But we can read it from `window.XMLHttpRequest`:
82+
Luckily, there's a "road out of hell", called "Javascript modules".
10083

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`.
10685

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`:
10887

109-
We can also write the test without `window`:
88+
```html run
89+
<script type="module">
90+
var x = 5;
11091

111-
```js
112-
if (typeof XMLHttpRequest == 'function') {
113-
/* is there a function XMLHttpRequest? */
114-
}
92+
alert(window.x); // undefined
93+
</script>
11594
```
11695

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:
11897

98+
```html run
99+
<script type="module">
100+
let x = 5;
101+
</script>
119102

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+
```
121108

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?):
123110

124-
That use is a little bit beyond our scope for now, but it looks like:
125111
```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
143114
</script>
144115
```
145116

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).
147120

148-
## "this" and global object
121+
## Valid uses of the global object
149122

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).
151124

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:
153126

154127
```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
157136
```
158137

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

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!");
166144
}
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.
167148
168-
f(); // called without an object
149+
```js run
150+
if (!window.Promise) {
151+
window.Promise = ... // custom implementation of the modern language feature
152+
}
169153
```
170154
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.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
```js run demo
2+
function* pseudoRandom(seed) {
3+
let value = seed;
4+
5+
while(true) {
6+
value = value * 16807 % 2147483647
7+
yield value;
8+
}
9+
10+
};
11+
12+
let generator = pseudoRandom(1);
13+
14+
alert(generator.next().value); // 16807
15+
alert(generator.next().value); // 282475249
16+
alert(generator.next().value); // 1622650073
17+
```
18+
19+
Please note, the same can be done with a regular function, like this:
20+
21+
```js run
22+
function pseudoRandom(seed) {
23+
let value = seed;
24+
25+
return function() {
26+
value = value * 16807 % 2147483647;
27+
return value;
28+
}
29+
}
30+
31+
let generator = pseudoRandom(1);
32+
33+
alert(generator()); // 16807
34+
alert(generator()); // 282475249
35+
alert(generator()); // 1622650073
36+
```
37+
38+
That's fine for this context. But then we loose ability to iterate with `for..of` and to use generator composition, that may be useful elsewhere.

1-js/10-generators/01-generator/01-pseudo-random-generator/task.md renamed to 1-js/06-advanced-functions/13-generators/01-pseudo-random-generator/task.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ If we use `1` as the seed, the values will be:
2323

2424
The task is to create a generator function `pseudoRandom(seed)` that takes `seed` and creates the generator with this formula.
2525

26-
Use example:
26+
Usage example:
2727

2828
```js
29-
let gen = pseudoRandom(1);
29+
let generator = pseudoRandom(1);
3030

31-
alert(gen.next().value); // 16807
32-
alert(gen.next().value); // 282475249
33-
alert(gen.next().value); // 1622650073
31+
alert(generator.next().value); // 16807
32+
alert(generator.next().value); // 282475249
33+
alert(generator.next().value); // 1622650073
3434
```

0 commit comments

Comments
 (0)