-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathUnderstanding ECMAScript 6.txt
661 lines (573 loc) · 24.3 KB
/
Understanding ECMAScript 6.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
Introduction
Chapter 1: How Block Bindings Work
bindings / variable:
"name" bound to "value" inside "scope"
var Declarations and Hoisting
Hoisting: var declaration treated as being at top of function or global scope (when outside of function)
No hoisting for var initialization
Ex. var declaration & initialization without showing hoisting
function getValue(condition) {
if(condition) {
var value = "blue"; // declaration & initialization
return value;
} else {
return null;
}
}
Ex. var declaration & initialization showing hoisting - what actually happen
function getValue(condition) {
var value; // declaration
if (condition) {
value = blue; // initialization
} else {
return null;
}
}
Block-Level Declarations
Block scope / lexical scope:
- inside a function
- inside a {} block
Block-Level declarations are inaccessible outside block scope
let Declarations
let declaration syntax is the same as var but only accessible in current block
Best practice: place let declaration at the begining of block
Ex.
function getValue(condition) {
// if condition: false, value will not be declared or initialized
if (conditon) {
let value = "blue"; // No hoisting - only accessible within current {} block
return value;
} else {
return null;
}
}
No Redeclaration
Same identifier, same scope, declared with let & var = error
Ex.
var count = 30;
let count = 40; // error - even if let declaration come first
Same identifier, let declaration within its own {} block = No error
Ex.
var count = 30;
if (condition) {
let count = 40; // local count "shadows" global count - no error
}
const Declarations
Binding with const:
- value cannot be changed once set
- (Thus) must be initialized on declaration
Ex.
const x = 30;
const y; // Uncaught SyntaxError: Missing initializer in const declaration
const vs. let Declarations
const declarations are block-level declarations - not accessible outside of scope
Error: const variable has the same identifier with another var or let variable within the same scope
const is read-only
const object's properties can be modified
Object declarations with const
Ex.
const person = { name: "Charles" };
person.name = "Jack"; // No error
// person = { name: "Jack" }; // SyntaxError: Identifier 'person' has already been declared
const prevents modification in binding, NOT modifification of bound value
The Temporal Dead Zone (TDZ)
Variables declared with let or const cannot be accessed until declaration
within the same scope
Ex.
console.log(typeof value); // undefined - no binding here
if (true) {
console.log(typeof value); // ReferenceError: value is not defined
let value = "red";
}
Block Bindings in Loops
declare counter with let to keep it inaccessible outside of the loop
Ex.
for (let i = 0; i < 10; i++) {
console.log(i);
}
console.log(i); // ReferenceError: i is not defined
Functions in Loops
declare counter with var - all iterations share one copy of the counter
Ex.
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func(); // "10" x 10
});
To keep unique counter value for each iteration, use immediate invoked function expressions (IIEF)
Ex.
var func = [];
for (var i = 0; i < 10; i++) {
// count i passed to IIFE for every iteration and stored as value
funcs.push((function(value) {
return function() {
console.log(value);
}
}(i)));
}
funcs.forEach(function(func) {
func(); // 0 to 9
});
let Declarations in Loops
Ex. use let in for loop
var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func();
});
const Declarations in Loops
const declaration cannot be used as counter in for loop - No increment allowed
Exception: for-in or for-of loop - b/c New binding created on each iteration
Ex. use let with for-in loop
var funcs = [],
object = {a: true, b: true, c: true};
for (let key in object) {
funcs.push(function() {
// value of key CANNOT be change inside loop - else eerror
console.log(key);
});
}
funcs.forEach(function(func) {
func(); // a, b, c
});
Global Block Bindings
Declaration with var in global scopr = create new property with identifier on global object
global object = window in browser
Ex.
var RegExp = "oy";
console.log(window.RegExp); // "oy"
var ncz = "Hi";
console.log(window.ncz); // "Hi"
Declaration with let or const in global scope =
create new binding in global scope (shadow)
but NO property added (overwrite) to global object
Ex.
let RegExp = "oy";
console.log(RegExp); // "oy"
console.log(window.RegExp === RegExp); // fasle - does not overwrite
const ncz = "yo";
console.log(ncz); // "yo"
console.log("ncz" in window); // false
var should still be used when code needs to be available from global object:
such as access code across frames or windows
Emerging Best Practices for Block Bindings
use let by default or use const by default
Summary
let and const block bindings are lexical scoping:
- no hoisted
- exist ONLY within block where they are declared
Block bindings:
- variables declared exactly where they are needed
- No access to variable before declaration - doing so cause error (b/c binding before declaration)
- declare let or const at the top of block scope to remove TDZ
Inside for-in for-of loop:
- let and const create new binding for with each new iteration
- (Thus) functions created inside loop body can access loop bindings' current values
rather than their values after loop's final iteration
- same for let in For loop
const declaration in for loop may result in an error
Current best practice: immutability prevent certain error
use const by default
use let only when the variable is expected to change
Chapter 2: Strings and Regular Expression
Better Unicode Support
code unit:
16-bit sequence that represents string before ECMAScript 6
code point:
global unique identifiers
number start from 0, each number represents a character
character codes
UTF-16 Code Points
character encoding: encode code points into code units, internally consistent
Basic Multilingual Plane (BMP):
First 2^16 code points in UTF-16 represent as single 16-bit code units
Supplementary planes: anything after 2^16 code
UTF-16 surrogate pairs:
single code point = 2 X 16-bit code units
Any character (code point) can be:
1. one code unit for BMP
2. two units of supplementary planes - total of 32 bits
Ex.
let text = "𠮷"; // character represented by a surrogate pair
console.log(text.length); // 2 - treated as two 16-bit characters, should be one
console.log(/^.$/.test(text)); // false - RegExp only matches single character
console.log(text.charAt(0)); // "" - first 16-bits does not corresponds to a printable character
console.log(text.charAt(1)); // "" - second 16-bits does not corresponds to a printable character
console.log(text.charCodeAt(0)); // 55362 - 16-bit number for first code unit
console.log(text.charCodeAt(1)); // 57271 - 16-bit number for second code unit
The codePointAt() Method of ES 6
codePointAt() returns code point maps to a given index (code unit position) in a string
Ex.
let text = "𠮷a";
console.log(text.charCodeAt(0)); // 55362 - 1st code unit of first character
console.log(text.charCodeAt(1)); // 57271 - 2nd code unit of first character
console.log(text.charCodeAt(2)); // 97 - 1st code unit of second character
console.log(text.codePointAt(0)); // 134071 - non-BMP character: 2 code units / 1 code point
console.log(text.codePointAt(1)); // 57271 - 2nd code unit of first character
console.log(text.codePointAt(2)); // 97 - 1st code unit of second character
Ex. check if a character is represented by 1 or 2 code units
function is32Bit(c) {
return c.codePointAt(0) > 0xFFFF; // upper bound of 16-bit = FFFF
}
console.log(is32Bit("𠮷")); // true
console.log(is32Bit("a")); // false
The String.fromCodePoint() Method
fromCodePoint() takes a code point and return a character in string
For BMP code point: fromCodePoint() has the same result as fromCharCode()
For non-BMP code point: fromCodePoint() and fromCharCode() have different result
Ex.
console.log(String.fromCodePoint(134071)); // "𠮷"
console.log(String.fromCharCode(134071)); // ஷ
The normalized() Method
Different characters can be considered equivalent for sorting or comparisons
Two types of equivalence:
1. canonical equivalence: two sequences of code points are interchangeable
2. compatibility: two sequences of code points are interchangeable in some situations
str.normalized([form]):
optionally takes one of the four Unicode normalization forms
returns string in noralized form
Unicode normalization forms:
1. Normalization Form Canonical Composition ("NFC") - default
2. Normalization Form Canonical Decomposition ("NFD")
3. Normalization Form Compatibility Composition ("NFKC")
4. Normalization Form Compatibility Decomposition ("NFKD")
When comparingstrings, both strings must be normalized to the SAME form
Ex.
let normalized = values.map(function(text) {
return text.normalize();
});
normalized.sort(function(first, second) {
if (first < second) {
return -1;
} else if (first === second) {
return 0;
} else {
return 1;
}
});
Ex. normalized() as part of comparator of sort function
values.sort(function(first, second) {
let firstNormalized = first.normalize(),
secondNormalized = second.normalize();
// alternatively, with NFD
// let firstNormalized = first.normalize("NFD"),
// secondNormalized = second.normalize("NFD");
if (firstNormalized < secondNormalized) {
return -1;
} else if (firstNormalized === secondNormalized) {
return 0;
} else {
return 1;
}
});
The Regular Expression u Flag
With out u Flag, RegExp matches on 16-bit code units
u: Unicode
With u Flag set:
RegExp matches on characters
Ex. /^.$/ matches any string with single character
let text = "𠮷";
console.log(text.length); // 2
console.log(/^.$/.test(text)); // false
console.log(/^.$/u.test(text)); // true - single character with 2 code units
Counting Code Points w/ RegExp
Ex.
function codePointLength(text) {
let result = text.match(/[\s\S]/gu);
return result ? result.length : 0;
}
// both sample texts have 3 characters
console.log(codePointLength("abc")); // 3
console.log(codePointLength("𠮷bc")); // 3
/[\s\S]/gu
pattern matches both whitespace(\s) and non-whitespace character (\S) - also newlines (\s)
flag g: apply globally (to entire string)
flag u: enable unicode - matches on characters
result contains an array of matches if there is any - array length = number of code points in string
!! Warning: Slow performance - use string iterator if possible !!
Verify Support for the u flag
Ex. detect u flag without syntax error
function hasRegExpU() {
try{
pattern = new RegExp(".", "u"); // throw exception if u flag is not supported
}
catch(e) {
return false;
}
}
Other String Changes
Methods for identifying Substrings
String.include()
str.includes(searchString[, position])
return true if searchString is found within str, else return false
optional: index within str where search begin (default to 0)
String.startsWith()
str.startsWith(searchString[, position])
return true if searchString is found at the begining of str, else return false
optional: index within str where search begin (default to 0)
String.endsWtih()
str.endsWtih(searchString[, length])
return true if searchString matches the end of str, else return false
optional: match from index {length - 1} to index {0} (default to str.length)
Ex.
let msg = "0123456";
console.log(msg.startsWith("012")); // true
console.log(msg.endsWith("6")); // true
console.log(msg.includes("3")); // true
console.log(msg.startsWith("2")); // false
console.log(msg.endsWith("56")); // true
console.log(msg.includes("x")); // fasle
console.log(msg.startsWith("2", 2)); // true
console.log(msg.endsWith("34", 5)); // true
console.log(msg.includes("2", 3)); // false
indexOf() and lastIndexOf() also takes RegExp, convert it into string then search for that string
However, include(), startsWith() and endsWith()
The repeat() Method
str.repeat(count);
count: integer [0, +∞)
return new string consisted of original string (str) repeated count time
Ex.
console.log("x".repeat(s));
Ex. code formating for indentation levels
// indent using a specified number of spaces
let indent = " ".repeat(4), indentLevel = 0;
// whenever indent level increses
let newIndent = indent.repeat(++indentLevel);
// second indent: 8 spaces and indentLevel: 2
let secondIndent = indent.repeat(++indentLevel);
Other Regular Expression Changes
The Regular Expression y Flag:
Start matching at the position specified by RegExp property "lastIndex"
Ex.
let text = "hello1 hello2 hello3",
pattern = /hello\d\s?/, // no flag
result = pattern.exec(text),
globalPattern = /hello\d\s?/g, // g flag
globalResult = globalPattern.exec(text),
stickyPattern = /hello\d\s?/y, // y flag
stickyResult = stickyPattern.exec(text);
console.log(result.length);
console.log(result[0]); // "hello1 "
console.log(globalResult.length);
console.log(globalResult[0]); // "hello1 "
console.log(stickyResult[0]); // "hello1 "
pattern.lastIndex = 1; // start matching from second character - no flag: ignored
globalPattern.lastIndex = 1; // start matching from second character - g flag: star search from second character 'e'
stickyPattern.lastIndex = 1; // start matching from second character - y flag: no match at index 1, return null when executed
result = pattern.exec(text);
globalResult = globalPattern.exec(text);
stickyResult = stickyPattern.exec(text); // null
console.log(result.length); // 3 Nope - 1 why?
console.log(result[0]); // "hello1 "
console.log(globalResult.length); // 2 Nope - 1 why?
console.log(globalResult[0]); // "hello2 "
console.log(stickyResult[0]); // throws an error!
Duplicating Regular expressions
The flags properties
Template Literals
Basic Syntax
Multiline Strings
Making Substitutions
Tagged Templates
Summary
Full unicode support in ECMAScript 6 = process UTF-16 characters logically
string manipulation: transfer b/t code point and character
codePointAt(pos): return a number represents code point value of character at given index
String.fromCodePoint(): return a string created from specified sequence of code points
Regular expression u flag enables operation on code points instead of 16-bit characters
normalize() method returns Unidoce Normalization Form of the string (for manipulation)
ECMAScript 6 new string methods:
identify substring
RegExp
Template literals:
Create domain-specific languages (DSLs) to create string easier
Embed variables into teplate literals
Built-in support of multiline strings
Newlines are allowed in template literal but \n and escape sequence are still functional
Template Tags:
functions that receives pieces of template literal as arguments, such as:
literals
raw equivalents
substitution values
then return string value
Chapter 3: Functions in ECMAScript 6
Chapter 4: Expanded Object Functionality
Chapter 5: Destructuring for Easier Data Access
Chapter 6: Symbols and Symbol Properties
Chapter 7: Sets and Maps
Chapter 8: Iterators and Generators
Chapter 9: Introducing JavaScript Classes
Chapter 10: Improved Array Capabilities
Chapter 11: Promises and Asynchronous Programming
Asynchronous Programming Background
The Event Model
The Callback Pattern
Promise Basics
promise is a placeholder for the result of asynchronous operation / function
ex.
// readFile promises to complete at some time in the future
let promise = readFile("example.txt")
WHEN the result of function will be ready depends on promise life cycle
The Promise Life Cycle
Starting in "Pending" state, indicates asynchronous operation is not completed yet (unsettled)
Once asynchronous operation completes (settled), promise enters one of the 2 states:
1. Fulfilled - operation completed successfully
2. Rejected - operation completed unsuccessfully (due to error or other causes)
Internal property, [[PromiseState]] is "pending", "fulfilled", or "rejected"
When [[PromiseState]] changes, then() method will be executed
then()
- Any object implements is a "thenable"
- Available in all promises
- Not ALL thenables are promises
- then() takes 2 OPTIONAL arguments
1. function to call when promise is fulfilled
2. function to call when promise is rejected
- any additional data related to asynchronous operation is passed to either function
- Without reject handler, all failures will happen silently - so ALWAYS attach a rejection handler
ex.
let promise = readFile("example.txt")
promise.then(function(contents){
// fulfillment
console.log(contents);
}, function(err) {
// rejection
console.error(err.message);
});
promise.then(function(contents) {
//fulfillment - ERROR will NOT be reported
console.log(contents);
});
promise.then(null, function() {
// rejection - success will not be reported
console.error(err.message);
});
// is the same as:
promise.catch(function(err) {
// rejection
console.error(err.message)
});
Promises also have catch(), intended to be used in combination to handle asynchronous operation
To add new fulfillment or rejection handler AFTER
promise settled and previous handler executed:
Ex.
let promise = readFile("example.txt");
// original fulfillment handler
promise.then(function(contents) {
console.log(contents);
// new fulfillment handler
promise.then(function(contents) {
console.log(contents);
});
});
Creating Unsettled Promises
Function like readFile() returns a Promise, created within function definition
Promise is created with constructor like:
new Promise()
Promise constructor takes a single function, "executor", as argument
Executor runs immediately when function is called
The executor takes 2 functions as arguments AND implement asynchronous operation
1. resolve() - called when asynchronous operation succeeded to add job to job cucue and resolve Promise
2. reject() - called when asynchornous operation failed to add job to job cucue and resolve Promise
Ex.
// Node.js example
let fs = require("fs")
// a function that returns readFile
function readFile(filename) {
// executor function takes resolve() and reject()
return new Promise(function(resolve, reject) {
// asnychronous operation
fs.readFile(fileName, {encoding: "utf8"}, function(err, contents) {
// call reject() when operation fails
if(err) {
reject(err);
return; // end method
}
// call resolve() when operation succeeded
resolve(contents);
});
});
}
let promise = readFile("example.txt")
// listen for both fulfillment and rejection
promise.then(function(contents) {
// fulfillment
console.log(contents);
}, function(err) {
// rejection
console.error(err.message);
});
Ex. Job scheduling
let promise = new Promise(function(resolve, reject) {
console.log("Promise");
resolve();
});
console.log("Hi!")
VM509:2 Promise
VM509:6 Hi!
undefined
let promise = new Promise(function(resolve, reject) {
console.log("Promise");
resolve();
});
promise.then( function() {
console.log("Resolved")
});
console.log("Hi!");
// The output is:
// Promise
// Hi!
// Resolved
Fulfillment and rejection handler are always added to the END of job queue AFTER executor is completed
Creating Settled Promise
To use Promise to represent ONLY a single known value:
1. Promise.resolve()
2. Promise.reject()
Pass Promise into either method, the Promise will be returned WITHOUT modification
Using Promise.resolve()
Ex.
let promise = Promise.resolve(42)
promise.then(function(value) {
console.log(value);
}); // 42
Using Promise.reject()
Ex.
let promise = Promise.reject(42);
promise.catch(function(value) {
console.log(value);
}); // 42
Passing Non-Promise Thanables to Promise.resolve() / Promise.reject()
Non-Promise Thanable:
An object with then() method that accepts
resolve and reject handler functions
Ex. for Promise.resolve()
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value);
})
Ex. for Promise.reject()
let thenable = {
then: function (resolve, reject) {
reject(42);
}
};
let p2 = Promise.resolve(thenable);
p2.catch( function(value) {
console.log(value)
});
Chapter 12: Proxies and the Reflection API
Chapter 13: Encapsulating Code with Modules
Appendix A: Smaller ECMAScript 6 Changes
Appendix B: Understanding ECMAScript 7 (2016)