You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/3-object-basics/1-object/article.md
+3-9Lines changed: 3 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -235,22 +235,16 @@ let obj = {
235
235
alert( obj.for + obj.let + obj.return ); // 6
236
236
```
237
237
238
-
Basically, any name is allowed, with one exclusion: `__proto__`.
239
-
240
-
The built-in property named `__proto__` has a special functionality (we'll cover it later), and it can't be set to a non-object value:
238
+
Basically, any name is allowed, but there's a special one: `"__proto__"`. We can't set it to a non-object value:
241
239
242
240
```js run
243
-
let obj = {};
244
-
obj.__proto__ = 5;
241
+
let obj = { __proto__: 5 };
245
242
alert(obj.__proto__); // [object Object], didn't work as intended
246
243
```
247
244
248
-
As you we see from the code, an assignment to a primitive is ignored. If we want to store *arbitrary* (user-provided) keys, then such behavior can be the source of bugs and even vulnerabilities, because it's unexpected. There's another data structure [Map](info:map-set-weakmap-weakset), that we'll learn in the chapter <info:map-set-weakmap-weakset>, which supports arbitrary keys.
245
+
Later we'll learn more about that `__proto__` and see how to work around it [todo Object.create(null)]. Also we'll learn another data structure [Map](info:map-set-weakmap-weakset) that doesn't have such problems and supports arbitrary keys.
249
246
````
250
247
251
-
252
-
253
-
254
248
## Existance check
255
249
256
250
A notable objects feature is that it's possible to access any property. There will be no error if the property doesn't exist! Accessing a non-existing property just returns `undefined`. It provides a very common way to test whether the property exists -- to get it and compare vs undefined:
Copy file name to clipboardExpand all lines: 1-js/6-data-structures/3-string/article.md
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -451,7 +451,9 @@ Let's recap the methods to avoid any confusion:
451
451
452
452
453
453
```smart header="Which one to choose?"
454
-
All of them can do the job. The author finds himself using `slice` almost all the time.
454
+
All of them can do the job. Formally, `substr` has a minor drawback: it is described not in the core Javascript specification, but in Annex B, which covers browser-only features that exist mainly for historical reasons. So, non-browser environments may fail to support it. But in practice it works everywhere.
455
+
456
+
The author finds himself using `slice` almost all the time.
455
457
```
456
458
457
459
## Comparing strings
@@ -526,7 +528,7 @@ The characters are compared by their numeric code. The greater code means that t
526
528
527
529
### Correct comparisons
528
530
529
-
The "right" algorithm to do string comparisons is more complex than it may seem. Because the alphabets are different for different languages. So the same letter may be located differently in different alphabets, that is -- even if it looks the same, different alphabets put it in different place.
531
+
The "right" algorithm to do string comparisons is more complex than it may seem. Because alphabets are different for different languages. The same-looking letter may be located differently in different alphabets.
530
532
531
533
So, the browser needs to know the language to compare.
Copy file name to clipboardExpand all lines: 1-js/7-deeper/2-closure/article.md
+123-1Lines changed: 123 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -420,6 +420,129 @@ There are other ways to do that:
420
420
}();
421
421
```
422
422
423
+
## Garbage collection
424
+
425
+
Lexical Environment objects that we've been talking about follow same garbage collection rules as regular ones.
426
+
427
+
So, a Lexical Environment exists while there's a nested function referencing it with its `[[Environment]]`.
428
+
429
+
- Usually, Lexical Environment is cleaned up after the function run. Even if it has a nested function, for instance:
430
+
431
+
```js
432
+
functionf() {
433
+
let value =123;
434
+
435
+
functiong() {} // g is local
436
+
}
437
+
438
+
f();
439
+
```
440
+
441
+
Here both `value` and `g` become unreachable after the end of`f()`, and so even though `g` references its outer lexical environment, that doesn't matter.
442
+
443
+
- But if `g` were returned and kept reachable, then that its reference keeps the outer lexical environment alive as well:
444
+
445
+
```js
446
+
function f() {
447
+
let value = 123;
448
+
449
+
function g() {}
450
+
451
+
*!*
452
+
return g;
453
+
*/!*
454
+
}
455
+
456
+
let g = f(); // function g will live and keep the outer lexical environment in memory
457
+
```
458
+
459
+
- If `f()` is called many times, and resulting functions are saved, then the corresponding Lexical Environment objects will also be retained in memory. All 3 of them in the code below:
460
+
461
+
```js
462
+
function f() {
463
+
var value = Math.random();
464
+
465
+
return function() {};
466
+
}
467
+
468
+
// 3 functions in array, every of them links to LexicalEnvrironment
469
+
// from the corresponding f() run
470
+
let arr = [f(), f(), f()];
471
+
```
472
+
- A Lexical Environment object lives until it is reachable. In the code below, after `g` becomes unreachable, it dies with it:
473
+
474
+
```js
475
+
function f() {
476
+
var value = 123;
477
+
478
+
function g() {}
479
+
480
+
return g;
481
+
}
482
+
483
+
let g = f(); // while g is alive
484
+
// there corresponding Lexical Environment lives
485
+
486
+
g = null; // ...and now the memory is cleaned up
487
+
```
488
+
489
+
### Real-life optimizations
490
+
491
+
As we've seen, in theory while a function is alive, all outer variabels are also retained.
492
+
493
+
But in practice, JS engines try to optimize that. They analyze variable usage and if it's easy to see that an outer variable is not used -- it is removed.
494
+
495
+
In the code above `value` is not used in `g`. So it will be cleaned up from the memory.
496
+
497
+
**Important side effect in V8 (Chrome, Opera) is that such variable will become unavailable in debugging!**
498
+
499
+
Try running the example below with the open Developer Tools in Chrome.
500
+
501
+
When it pauses, in console type `alert(value)`.
502
+
503
+
```js run
504
+
function f() {
505
+
var value =Math.random();
506
+
507
+
functiong() {
508
+
debugger; // in console: type alert( value ); No such variable!
509
+
}
510
+
511
+
return g;
512
+
}
513
+
514
+
var g =f();
515
+
g();
516
+
```
517
+
518
+
As you could see -- there is no such variable! The engine decided that we won't need it and removed it.
519
+
520
+
That may lead to funny (if not such time-consuming) debugging issues, especially when we can see instead of *our* variable the more outer one:
521
+
522
+
```js run global
523
+
let value ="Surprise!";
524
+
525
+
functionf() {
526
+
let value ="the closest value";
527
+
528
+
functiong() {
529
+
debugger; // in console: type alert( value ); Surprise!
530
+
}
531
+
532
+
return g;
533
+
}
534
+
535
+
let g =f();
536
+
g();
537
+
```
538
+
539
+
```warn header="See ya!"
540
+
This feature of V8 is good to know. If you are debugging with Chrome/Opera, sooner or later you will meet it.
541
+
542
+
That is not a bug of debugger, but a special feature of V8. Maybe it will be changed sometimes.
543
+
You always can check for it by running examples on this page.
544
+
```
545
+
423
546
424
547
## The old "var"
425
548
@@ -586,7 +709,6 @@ The features described above make using `var` inconvenient most of time. First,
586
709
587
710
588
711
589
-
590
712
## Global object
591
713
592
714
A *global object* is the object that provides access to built-in functions and values, defined by the specification and the environment.
Copy file name to clipboardExpand all lines: 1-js/7-deeper/3-function-object/article.md
+85-57Lines changed: 85 additions & 57 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -170,11 +170,11 @@ alert( counter() ); // 0
170
170
alert( counter() ); // 1
171
171
```
172
172
173
-
Unlike the closures, the `count` is now bound to the function directly, not to its outer Lexical Environment.
173
+
Unlike examples from chapter <info:closure>, the `count` is now stored in the function directly, not in its outer Lexical Environment.
174
174
175
175
Is it worse or better than using the closure?
176
176
177
-
The main difference is that if the value of `count` lives in an outer variable, then an external code is unable to access it. Only the nested function may modify it. And if it's bound to function, then such thing is possible:
177
+
The main difference is that if the value of `count` lives in an outer variable, then an external code is unable to access it. Only nested functions may modify it. And if it's bound to function, then such thing is possible:
178
178
179
179
```js run
180
180
functionmakeCounter() {
@@ -196,108 +196,136 @@ alert( counter() ); // 10
196
196
*/!*
197
197
```
198
198
199
-
Sometimes such possibility can be a plus, but usually we want more control over `count`, and hence the closure is more often used.
199
+
So it depends on our aims which variant to choose.
200
200
201
201
## Named Function Expression
202
202
203
-
Compare these two function definitions:
203
+
Named Function Expression or, shortly, NFE, is a term a Function Expression that has a name.
204
+
205
+
For instance, let's take an ordinary Function Expression:
204
206
205
207
```js
206
-
letsayHi=function() {// (1)
207
-
alert('Hello');
208
+
letsayHi=function(who) {
209
+
alert(`Hello, ${who}`);
208
210
};
211
+
```
212
+
213
+
...And add a name to it:
209
214
210
-
letsayHi=function*!*func*/!*() { // (2)
211
-
alert('Hello');
215
+
```js
216
+
letsayHi=function*!*func*/!*(who) {
217
+
alert(`Hello, ${who}`);
212
218
};
213
219
```
214
220
215
-
Both create a function and put it into the variable `sayHi`. And usually we use the first variant is fine.
221
+
What's the role of that additional `"func"` name?
216
222
217
-
But if we specify a name right in the Function Expression (2), then it becomes an "internal function name", only visible from inside the function.
223
+
First let's note, that we still have a Function Expression. Adding the name `"func"` after `function` did not make it a Function Declaration, because it is still created as a part of an assignment expression.
218
224
219
-
Let's see why we may need it.
225
+
Adding such a name also did not break anything.
220
226
221
-
As we've seen it's easy to copy a function and maybe replace the previous value with something else:
227
+
The function is still available as `sayHi()`:
222
228
223
-
```js run
224
-
letsayHi=function() {
225
-
alert('Hello');
229
+
```js run
230
+
letsayHi=function*!*func*/!*(who) {
231
+
alert(`Hello, ${who}`);
226
232
};
227
233
228
-
// oh maybe another word is better? replace it!
229
-
let oldSayHi = sayHi; // keep the old variant here
230
-
sayHi=function() { // replace with a newer one
231
-
alert("What's up dude?");
234
+
sayHi("John"); // Hello, John
235
+
```
236
+
237
+
There are two special things about the name `func`:
238
+
239
+
1. It allows to reference the function from inside itself.
240
+
2. It is not visible outside of the function.
241
+
242
+
For instance, the function `sayHi` below re-calls itself with `"Guest"` if no `who` is provided:
243
+
244
+
```js run
245
+
letsayHi=function*!*func*/!*(who) {
246
+
if (who) {
247
+
alert(`Hello, ${who}`);
248
+
} else {
249
+
*!*
250
+
func("Guest"); // use func to re-call itself
251
+
*/!*
252
+
}
232
253
};
233
254
255
+
sayHi(); // Hello, Guest
234
256
235
-
oldSayHi(); //Hello
236
-
sayHi(); //What's up dude?
257
+
//But this won't work:
258
+
func(); //Error, func is not defined (not visible outside of the function)
237
259
```
238
260
239
-
The problem may occur if a function references *itself* from inside. It happens when the function wants to access its properties (`sayHi.counter` in the example above), or it wants to recursively call itself one more time.
261
+
Why do we use `func`? Maybe just use `sayHi` for the nested call?
240
262
241
-
But if the function has moved, then the old name becomes irrelevant! There will be an error.
242
263
243
-
Here's the code:
264
+
Actually, in most cases we can:
244
265
245
-
```js run
246
-
// create a function
247
-
letsayHi=function() {
248
-
sayHi.counter++;
249
-
alert('Hi '+sayHi.counter);
266
+
```js
267
+
letsayHi=function(who) {
268
+
if (who) {
269
+
alert(`Hello, ${who}`);
270
+
} else {
271
+
*!*
272
+
sayHi("Guest");
273
+
*/!*
274
+
}
250
275
};
251
-
sayHi.counter=0;
276
+
```
252
277
253
-
// move it
254
-
let movedSayHi = sayHi;
255
-
256
-
// overwrite the old name to make things more obvious
257
-
sayHi =null;
278
+
The problem with that code is that the value of `sayHi` may change. The function may go to another variable, and the code will start to give errors:
258
279
280
+
```js run
281
+
letsayHi=function(who) {
282
+
if (who) {
283
+
alert(`Hello, ${who}`);
284
+
} else {
259
285
*!*
260
-
movedSayHi(); // Error: Cannot read property 'counter' of null
286
+
sayHi("Guest"); // Error: sayHi is not a function
261
287
*/!*
288
+
}
289
+
};
290
+
291
+
let welcome = sayHi;
292
+
sayHi =null;
293
+
294
+
welcome(); // Error, the nested sayHi call doesn't work any more!
262
295
```
263
296
264
-
The optional name which we can put into the Function Expression is exactly meant to solve this kind of problems.
297
+
That happens because the function takes `sayHi` from its outer lexical environment. There's no local `sayHi`, so the outer variable is used. And at the moment of the call that outer `sayHi` is `null`.
265
298
266
-
- It is only visible from inside the function.
267
-
- It always references the current function.
299
+
The optional name which we can put into the Function Expression is exactly meant to solve this kind of problems.
268
300
269
301
Let's use it to fix the code:
270
302
271
303
```js run
272
-
// now with the internal name "say"
273
-
letsayHi=function*!*say*/!*() {
274
-
*!*say*/!*.counter++;
275
-
alert('Hi '+*!*say*/!*.counter); // and use it everywhere inside
304
+
letsayHi=function*!*func*/!*(who) {
305
+
if (who) {
306
+
alert(`Hello, ${who}`);
307
+
} else {
308
+
*!*
309
+
func("Guest"); // Now all fine
310
+
*/!*
311
+
}
276
312
};
277
-
sayHi.counter=0;
278
-
279
-
let movedSayHi = sayHi;
280
313
314
+
let welcome = sayHi;
281
315
sayHi =null;
282
316
283
-
movedSayHi(); // Hi 1
284
-
movedSayHi(); // Hi 2 (works)
285
-
286
-
alert(say); // Error (say is undefined, that's normal)
317
+
welcome(); // Hello, Guest (nested call works)
287
318
```
288
319
289
-
Please note that:
290
-
291
-
- The name `say` exists only inside the function. The last line demonstrates that.
292
-
- The name `say` inside the function is always the current function, no matter in which variable it is. That's why it works.
320
+
Now it works, because the name `"func"` is function-local. It is not taken from outside (and not visible there). The specification guarantees that it always references the current function.
293
321
294
-
So the outer code has it's variable `sayHi` or `movedSayHi` later to call the function. The `say` is an "internal function name", how it calls itself privately.
295
-
296
-
A Function Expression with a name is called *Named Function Expression*, often abbreviated as NFE.
322
+
The outer code still has it's variable `sayHi` or `welcome` later. And `func` is an "internal function name", how it calls itself privately.
297
323
324
+
```smart header="No such thing for Function Declaration"
298
325
The "internal name" feature described here is only available for Function Expressions, not to Function Declarations. For Function Declarations, there's just no syntax possibility to add a one more "internal" name for them.
299
326
300
327
Sometimes, when we need a reliable internal name, it's the reason to rewrite a Function Declaration to Named Function Expression form.
0 commit comments