Chapter1 Block Bindings
1.1 Var Declarations and Hoisting
Hoisting for var
- If in function: as if they are at the top of the function
- If outside of function: at the top of global scope
- Not at the top of block
1 | function alertValue(condition) { |
1.2 Block-Level Declarations
- Block scope is created by:
- Inside a function
- Inside of a block (indicated by
{
and}
- let (like other language)
- In same scope
- no redeclaration
- can revalue
- In different scope
- cover
- In same scope
const
- similar as
let
except: cannot re-reference, while update object is fine
1
2
3const person = { name: "Nicholas"};
person.name = "Greg"; // works
person = { name: "Nicholas" }; // error
- similar as
The Temporal Dead Zone
- var: hoist
let/const (in same scope)
- Places the declaration in the TDZ
- Any attempt to access a variable in the TDZ results in a runtime error
- That variable is only removed from the TDZ, and therefore safe to use, once execution flows to the variable declaration
This even affect
typeof
1
2
3
4
5
6console.log(typeof value); // "undefined"
if (condition) {
console.log(typeof value); // reference error
let value = "blue";
}
1.3 Block Binding in Loops
declaration in loop
1
2
3
4
5for (let i = 0; i < 10; i++) {}
console.log(i); // error
for (var j = 0; j < 10; j++) {}
console.log(j) // 10const
infor-in
andfor-of
1
2
3for (const i = 0; i < 10; i++) {} // error
for (const i in obj) {} // fine
function in loop
1
2
3
4
5
6
7var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
func(); // outputs the number "10" ten times
});1
2
3
4
5
6
7var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
func(); // expect output
});
1.4 Global Block Bindings
var
overwrites window attributelet
/const
declare a new one in scope
Question: in global, what’s the difference between
var a = 123
andb = 123
1.5 Emerging Best Practices for Block Bindings
- default using
const
if we can - then use
let
if it might change
Chapter2 Strings and Regular Expressions
2.1 Better Unicode Support
Skip
2.2 Other String Changes
- Identifying Substrings
- includes()
- startsWith()
- endsWith()
- repeat()
2.3 Other Regular Expression Changes
- y Flag: sticky property
- it tells the search to start matching characters in a string at the position specified by the regular expression’s lastIndex property.
- lastIndex:
pattern.lastIndex
- sticky:
pattern.sticky
- Duplicating RE:
let re = new RegExp(re1, "g");
flags
property1
2
3let re = /ab/g;
console.log(re.source);
console.log(re.flags);
2.4 Template Literals
Multiline strings
1
2
3
4let html = `
<div>
<h1>Title</h1>
</div>`.trim()Basic string formatting
Substitution
1
2
3
4
5let count = 10,
price = 0.25,
message = `${count} items cost $${(count * price).toFixed(2)}.`;
console.log(message);Tagged Templates: advanced substitution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20function passthru(literals, ...substitutions) {
let result = "";
// run the loop only for the substitution count
for (let i = 0; i < substitutions.length; i++) {
result += literals[i];
result += substitutions[i];
}
// add the last literal
result += literals[literals.length - 1];
return result;
}
let count = 10,
price = 0.25,
message = passthru`${count} items cost $${(count * price).toFixed(2)}.`;
console.log(message); // "10 items cost $2.50."
HTML escaping
String.raw
1
2
3
4
5
6let message1 = `Multiline\nstring`,
message2 = String.raw`Multiline\nstring`;
console.log(message1); // "Multiline
// string"
console.log(message2); // "Multiline\nstring"
Chapter3 Functions
3.1 Functions with Default Parameter Values
- example:
function makeRequest(url, timeout = 2000, callback){}
- The arguments object will not reflect changes to the named parameters.
- Default Parameter Expressions
function add(first, second = getValue()) { return first + second;}
function add(first, second = first)
- Default Parameter Value Temporal Dead Zone
- When executing add(), first, second are added into TDZ
- Initialize first then second one by one
- if
add(first=second, second)
will raise exception
3.2 Working with Unnamed Parameters
function pick(object, ...keys){}
3.3 Increased Capabilities of the Function Constructor
let add = new Function('first', 'second', 'return first+second')
3.4 The Spread Operator
example
1
2let values = [1, 2, 3];
console.log(Math.max(...values, 0));
3.5 ECMAScript 6’s name Property
function’s property
1
2function doSomething(){}
console.log(doSomething.name);
3.6 Clarifying the Dual Purpose of Functions
Two different function
- call function with
new
- [[Construct]] method is excuted with
this
set
- [[Construct]] method is excuted with
call function without
new
[[Call]] method is executed
1
2
3
4
5
6
7
8
9function Person(name) {
this.name = name;
}
var person = new Person("Nicholas");
var notAPerson = Person("Nicholas");
console.log(person); // "[Object object]"
console.log(notAPerson); // "undefined"
- call function with
The new.target MetaProperty: solve above issue
1
2
3
4
5
6
7function Person(name) {
if (typeof new.target !== "undefined") {
this.name = name;
} else {
throw new Error("use new");
}
}
3.7 Block-Level Functions
Difference between strict mode or not
1
2
3
4
5
6if (true) {
console.log(typeof doSomething); // function
let doSomething = function () {}
}
console.log(typeof doSomething); // throw error in strict mode
console.log(typeof doSomething); // function in nonstrict mode
3.8 Arrow Functions
- No this, super, arguments, and new.target bindings
- Cannot be called with new
- No prototype
- Can’t change this
- No arguments object
- No duplicate named parameters
example
1
2
3
4
5
6let person = function(name) {
return {
getName: function() {return name;}
};
}("Nicholas");
console.log(person.getName()); // "Nicholas"
3.9 Tail Call Optimization
- With this optimization, instead of creating a new stack frame for a tail call, the current stack frame is cleared and reused
- pre-conditions
- The tail call does not require access to variables in the current stack frame (meaning the function is not a closure)
- The function making the tail call has no further work to do after the tail call returns
- The result of the tail call is returned as the function value
example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// before
function factorial(n) {
if (n <= 1) {
return 1;
} else {
// not optimized - must multiply after returning
return n * factorial(n - 1);
}
}
// after
function factorial(n, p = 1) {
if (n <= 1) {
return 1 * p;
} else {
let result = n * p;
// optimized
return factorial(n - 1, result);
}
}
Chapter4 Expanded Object Functionality
4.1 Object Categories
Name | Explanation |
---|---|
Ordinary objects | Have all the default internal behaviors for objects in JavaScript. |
Exotic objects | Have internal behavior that differs from the default in some way. |
Standard objects | Are those defined by ECMAScript 6, such as Array, Date, and so on. Standard objects may be ordinary or exotic. |
Built-in objects | Are present in a JavaScript execution environment when a script begins to execute. All standard objects are built-in objects. |
4.2 Object Literal Syntax Extensions
Property Initializer Shorthand
1
2
3
4
5
6function createPerson(name, age) {
return {name: name, age: age};
}
function createPerson(name, age) {
return {name, age};
}
Concise Methods
1
2
3var person = {
sayName() { console.log(this.name);}
};
Computed Property Names
1
2
3
4
5
6// syntax error in ES5
var person = {
"first name": "Nicholas"
};
person["first name"] = "John";
person["first" + suffix] = "Nicholas";
4.3 New Methods
Name | Description |
---|---|
Object.is() | Similar as ===, but better for (+0, -0) and (NaN, NaN) |
Object.assign() | one object receives properties and methods from another object |
4.4 Duplicate Object Literal Properties
No error even in strict mode
1
2
3
4
5
6
7
8;
var person = {
name: "Nicholas",
name: "Greg" // no error in ES6 strict mode
};
console.log(person.name); // "Greg"
4.5 Own Property Enumeration Order
- This affects how properties are returned using
- Object.getOwnPropertyNames()
- Reflect.ownKeys (Chapter 12)
- Object.assign()
- Rule
- All numeric keys in ascending order
- All string keys in the order in which they were added to the object
- All symbol keys (Chapter 6) in the order in which they were added to the object
4.6 More Powerful Prototypes
Changing an Object’s Prototype
No standard way to change an object’s prototype after instantiation in ES5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21let person = {
getGreeting() {
return "Hello";
}
};
let dog = {
getGreeting() {
return "Woof";
}
};
// prototype is person
let friend = Object.create(person);
console.log(friend.getGreeting()); // "Hello"
console.log(Object.getPrototypeOf(friend) === person); // true
// set prototype to dog
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting()); // "Woof"
console.log(Object.getPrototypeOf(friend) === dog); // true
Easy Prototype Access with Super References
Introduce
super
keyword1
2
3
4
5
6
7
8
9
10
11let friend = {
getGreeting() {
return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";
}
};
// the same as
let friend = {
getGreeting() {
return super.getGreeting() + ", hi!";
}
};
Attempting to use super outside of concise methods results in a syntax error
1
2
3
4
5
6let friend = {
getGreeting: function() {
// syntax error
return super.getGreeting() + ", hi!";
}
};
4.7 A Formal Method Definition
- ECMAScript 6 formally defines a method as a function
- This function has an internal
[[HomeObject]]
property containing the object to which the method belongs - The
[[HomeObject]]
forgetGreeting()
method isperson
1
2
3
4
5
6
7
8
9
10
11let person = {
// method
getGreeting() {
return "Hello";
}
};
// not a method: because not in an object
function shareGreeting() {
return "Hi!";
}
Advanced knowledge: Any reference to
super
uses the[[HomeObject]]
to determine what to do- Call
Object.getPrototypeOf()
on the[[HomeObject]]
to retrieve a reference to the prototype.[[HomeObject]]
offriend.getGreeting()
isfriend
- the prototype is searched for a function with the same name.
- the prototype of
friend
isperson
- the prototype of
the
this
binding is set and the method is called.- bind
this
toperson
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15let person = {
getGreeting() {
return "Hello";
}
};
// prototype is person
let friend = {
getGreeting() {
return super.getGreeting() + ", hi!";
}
};
Object.setPrototypeOf(friend, person);
console.log(friend.getGreeting()); // "Hello, hi!"- bind
- Call
Chapter5 Destructuring for Easier Data Access
5.1 Why is Destructuring Useful?
The ECMAScript 6 implementation actually use syntax for object and array literals.
5.2 Object Destructuring
Destructuring Assignment
- All destructuring should be initialized when declaring
1
2
3let { type, name } = node;
({ type, name } = node); //change the values of variables
outputInfo({ type, name } = node);
- All destructuring should be initialized when declaring
Default Values
1
let { type, name, value = true } = node;
Assigning to Different Local Variable Names
1
2
3let { type: localType, name: localName = "bar" } = node; // default value
console.log(localName); // "bar"
Nested Object Destructuring
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20let node = {
type: "Identifier",
name: "foo",
loc: {
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 4
}
}
};
// extract node.loc.start
let { loc: { start: localStart }} = node;
console.log(localStart.line); // 1
console.log(localStart.column); // 1
5.3 Array Destructuring
Destructuring Assignment
1
2
3
4
5let colors = [ "red", "green", "blue" ];
let [ , , thirdColor ] = colors;
[firstColor, secondColor, thirdColor] = colors; // no bracket compared with obj destruct
[ a, b ] = [ b, a ]; // swap variableDefault Values
1
let [ firstColor, secondColor = "green" ] = colors;
Nested Destructuring
1
2
3
4let colors = [ "red", [ "green", "lightgreen" ], "blue" ];
// later
let [ firstColor, [ secondColor ] ] = colors;
Rest Items
1
2
3let colors = [ "red", "green", "blue" ];
let [ firstColor, ...restColors ] = colors;
5.4 Mixed Destructuring
- Combination of
Object Destructuring
andArray Destructuring
Use case: fetch info from
JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24let node = {
type: "Identifier",
name: "foo",
loc: {
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 4
}
},
range: [0, 3]
};
let {
loc: { start },
range: [ startIndex ]
} = node;
console.log(start.line); // 1
console.log(start.column); // 1
console.log(startIndex); // 0
5.5 Destructured Parameters
It supports everything in this chapter, including:
- default values
- mix object and array patterns
use variable names that differ from the properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function setCookie(name, value, { secure, path, domain, expires }) {
// code to set the cookie
}
function setCookie(name, value, {
secure = false,
path = "/",
domain = "example.com",
expires = new Date(Date.now() + 360000000
}) {
// with default value
}
setCookie("type", "js", {
secure: true,
expires: 60000
});
Chapter6 Symbols and Symbol Properties
The private names
proposal eventually evolved into ECMAScript 6 symbols
6.1 Creating Symbols
Creating and Typeof
1
2
3let firstName = Symbol("first name");
console.log(firstName); // print firstName.toString(), which is [[Description]]
console.log(typeof firstName); // "symbol"
6.2 Using Symbols
computed object literal property names
1
2
3let person = {
[firstName]: "Nicholas"
};Object.defineProperty()
1
Object.defineProperty(person, firstName, { writable: false });
6.3 Sharing Symbols
create a shared symbol
1
2
3
4let uid = Symbol.for("uid");
let uid2 = Symbol.for("uid");
console.log(uid === uid2); // trueaccess global shared symbol key
1
console.log(Symbol.keyFor(uid2)); // "uid"
6.4 Symbol Coercion
- Don’t do it
6.5 Retrieving Symbol Properties
Object.keys()
andObject.getOwnPropertyNames()
don’t support Symbol propertyInstead
Object.getOwnProperty-Symbols()
1
2
3
4
5
6
7
8
9
10let uid = Symbol.for("uid");
let object = {
[uid]: "12345"
};
let symbols = Object.getOwnPropertySymbols(object);
console.log(symbols.length); // 1
console.log(symbols[0]); // "Symbol(uid)"
console.log(object[symbols[0]]); // "12345"
6.6 Exposing Internal Operations with Well-Known Symbols
An example
1
2
3
4
5
6let collection = {
0: "Hello",
1: "world",
length: 2,
[Symbol.isConcatSpreadable]: true
};Full list
Symbol | description |
---|---|
Symbol.hasInstance | A method used by instanceof to determine an object’s inheritance. |
Symbol.isConcatSpreadable | A Boolean value indicating that Array.prototype.concat() should flatten the collection’s elements if the collection is passed as a parameter to Array.prototype.concat() . |
Symbol.iterator | A method that returns an iterator. (Chapter 8) |
Symbol.match | Used by String.prototype.match() to compare strings. |
Symbol.replace | Uused by String.prototype.replace() to replace substrings. |
Symbol.search | Used by String.prototype.search() to locate substrings. |
Symbol.species | The constructor for making derived objects. (Chapter 9) |
Symbol.split | Used by String.prototype.split() to split up strings. |
Symbol.toPrimitive | A method that returns a primitive value representation of an object. |
Symbol.toStringTag | Used by Object.prototype.toString() to create an object description. |
Symbol.unscopables | An object whose properties are the names of object properties that should not be included in a with statement. |
Chapter7 Sets and Maps
7.1 Sets and Maps in ECMAScript 5
- Example
1
2
3
4
5
6let set = Object.create(null);
set.foo = true; // set
set.foo = bar; // map
if (set.foo) {
}
7.2 Problems with Workarounds
- a[0] and a[“0”]: different type but as same key
- a[{}] and a[{}]: different obj but as same key
7.3 Sets in ECMAScript 6
- Init:
let a = new Map();
- Methods:
- add()
- size
- has()
- delete()
- clear()
forEach(function(next, key, ownerSet), thisArg)
next
always equal tokey
ownerSet
: set itselfthisArg
: definedthis
in function- Use an arrow function to get the same effect without passing
thisArg
-
1
2
3process(dataSet) {
dataSet.forEach((value) => this.output(value));
}1
2
3
4
5process(dataSet) {
dataSet.forEach(function(value) {
this.output(value);
}, this);
}
Set vs Array
convert | implement |
---|---|
Set -> Array | [...set] |
Array -> Set | new Set(arr); |
Weak Set
Issue with Set: garbage collection
1
2
3
4
5
6
7
8
9
10
11let set = new Set(),
key = {};
set.add(key);
key = null;
console.log(set.size); // 1
// get the original reference back
key = [...set][0];Init weak set:
new WeakSet()
- Difference:
add()
can only be object- Not iterable: cannot use
for-of
(link 8.4) - Do not expose iterator: e.g. no
keys()
,values()
- No
forEach()
- No
size
attribute
7.4 Maps in ECMAScript 6
- Init:
let a = new Map();
let a = new Map([["kk", "vv"], "kkk", "vvv"]]);
- Methods:
- set()
- get()
- size
- has()
- delete()
- clear()
- forEach(): similar as Set, but callback
function(key, value, ownerMet)
Weak Set: similar as Set
- Only key is weak reference, not value
Use case:
- associated data with DOM elements
- key is removed authomatically while it is removed from DOM
- never mind
private attribute: amazing !!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14let Person = (function() {
let privateData = new WeakMap();
function Person(name) {
privateData.set(this, { name: name });
}
Person.prototype.getName = function() {
return privateData.get(this).name;
};
return Person;
}());
- associated data with DOM elements
Chapter8 Iterators and Generators
Use case:
for-of
- spread operator:
...
- Asynchronous
8.1 The Loop Problem
8.2 What are Iterators?
- Iterators are just objects with a specific interface designed for iteration.
- All iterator objects have a next() method that returns
- {value: value, done: bool}
8.3 What Are Generators?
- Definition
- indicated by a star character (*) after the function keyword
- use the new
yield
keywordyield
can only be used in generator or syntax error will show up
- fetch value by
next()
Example
function keyword
1
2
3
4
5
6
7
8function *createIterator() {
yield 1;
yield 2;
yield 3;
}
let iterator = createIterator();
console.log(iterator.next().value); // 1Generator Function Expression
1
2
3let createIterator = function *(items) {
yield items[i];
};Generator Object Methods
1
2
3
4
5
6
7
8
9
10
11
12var o = {
createIterator: function *(items) {
yield items[i];
}
};
// method shorthand
var o = {
*createIterator(items) {
yield items[i];
}
};
8.4 Iterables and for-of
Iterables: An iterable is an object with a
Symbol.iterator
property.for-of
- for-of loop first calls the
Symbol.iterator
method on array/set/map to retrieve an iterator. - for-of loop calls
next()
on an iterable each time the loop executes stores the value from the result object in a variable
1
2
3
4let values = [1, 2, 3];
let iterator = values[Symbol.iterator]();
console.log(iterator.next());
- for-of loop first calls the
- Creating Iterables
1
2
3
4
5
6
7
8
9let collection = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
}
};
8.5 Built-in Iterators
- entries(): Map default
- keys()
- values(): Set, Array default
Example
1
2
3for (let entry of map.entries()){}
// the same as
for (let entry of map){} // Destructing: for (let [k, v] of map) {}String
,NodeList
are also iterable
8.6 The Spread Operator and Non-Array Iterables
-
1
2
3let smallNumbers = [1, 2, 3],
bigNumbers = [100, 101, 102],
allNumbers = [0, ...smallNumbers, ...bigNumbers];
8.7 Advanced Iterator Functionality
Passing Arguments to Iterators
- The first call to next() is lost.
arguments passed to next() become the values returned by yield
1
2
3
4
5
6
7
8
9
10function *createIterator() {
let first = yield 1;
let second = yield first + 2; // 4 + 2
yield second + 3; // 5 + 3
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
Throwing Errors in Iterators
console.log(iterator.throw(new Error("Boom")));
- can have try-catch in generator
Generator Return Statements
- indicates that all processing is done, so the done property is set to true
- the value, if provided, becomes the value field
Delegating Generators
1
2
3
4
5function *createCombinedIterator() {
yield *createNumberIterator();
yield *createColorIterator();
yield 123;
}
8.8 Asynchronous Task Running
Use case:
- run asynchronous task by order
- callback for asynchronous task
Principle
- Store a series of asyn function in generator
- Only after yeilding the result of an asyn function, we do next one
Final
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
47let fs = require("fs");
function readFile(filename) {
return function(callback) { // the callback for result.value
fs.readFile(filename, callback);
};
}
function run(taskDef) {
// create the iterator, make available elsewhere
let task = taskDef();
// start the task
let result = task.next();
function step() {
if (!result.done) {
if (typeof result.value === "function") { // check if need to run callback
result.value(function(err, data) {
if (err) {
result = task.throw(err);
return;
}
result = task.next(data);
step();
});
} else {
result = task.next(result.value);
step();
}
}
}
// start the process
step();
}
run(function*() {
let contents = yield readFile("config.json");
doSomethingWith(contents);
console.log("Done");
});
Chapter9 Introducing JavaScript Classes
9.1 Class-Like Structures in ECMAScript 5
1
2
3
4
5
6
7
8
9
10
11
12
13function PersonType(name) {
this.name = name;
}
PersonType.prototype.sayName = function() {
console.log(this.name);
};
let person = new PersonType("Nicholas");
person.sayName(); // outputs "Nicholas"
console.log(person instanceof PersonType); // true
console.log(person instanceof Object); // true
9.2 Class Declarations
9.2.1 Example
example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class PersonClass {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}
let person = new PersonClass("Nicholas");
person.sayName(); // outputs "Nicholas"
console.log(person instanceof PersonClass); // true
console.log(person instanceof Object); // true
console.log(typeof PersonClass); // "function"
console.log(typeof PersonClass.prototype.sayName); // "function"The PersonClass declaration actually creates a function that has the behavior of the constructor method, which is why
typeof PersonClass
gives “function” as the result- The
sayName()
method also becomes a method onPersonClass.prototype
9.2.2 important difference with custom types
- Class declarations are not hoisted. Act like
let
declarations and so exist in the temporal dead zone until execution reaches the declaration. - All code inside of class declarations runs in strict mode automatically.
- All methods are non-enumerable.
- All methods lack an internal
[[Construct]]
method and will throw an error if you try to call them withnew
. - Calling the class constructor without new throws an error.
- Attempting to overwrite the class name within a class method throws an error.
Equivalent of PersonClass, this is awesome
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23let PersonType2 = (function() {
;
const PersonType2 = function(name) {
// make sure the function was called with new
if (typeof new.target === "undefined") {
throw new Error("Constructor must be called with new.");
}
this.name = name;
}
Object.defineProperty(PersonType2.prototype, "sayName", {
value: function() {
// make sure the method wasn't called with new
if (typeof new.target !== "undefined") {
throw new Error("Method cannot be called with new.");
}
console.log(this.name);
},
enumerable: false,
writable: true,
configurable: true
});
return PersonType2;
}());
9.3 Class Expressions
-
1
2
3
4let PersonClass = class {
//...
}
let p = new PersonClass("Nicholas");
9.4 Classes as First-Class Citizens
first-class citizen means:
- it can be used as a value
- it can be passed into a function
- returned from a function
assigned to a variable
1
2
3
4
5
6
7
8
9
10
11
12function createObject(classDef) {
return new classDef();
}
let obj = createObject(class {
sayHi() {
console.log("Hi!");
}
});
obj.sayHi();
creating singletons
1
let person = new class {...}("Nicholas");
9.5 Accessor Properties
getter
andsetter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class CustomHTMLElement {
constructor(element) {
this.element = element;
}
get html() {
return this.element.innerHTML;
}
set html(value) {
this.element.innerHTML = value;
}
}
var descriptor = Object.getOwnPropertyDescriptor(CustomHTMLElement.prototype, "ht\
ml");
console.log("get" in descriptor); // true
console.log("set" in descriptor); // true
console.log(descriptor.enumerable); // false
9.6 Computed Member Names
- Nothing special
1
2
3
4
5
6
7
8
9
10
11
12let propertyName = "html";
class CustomHTMLElement {
constructor(element) {
this.element = element;
}
get [propertyName]() {
return this.element.innerHTML;
}
set [propertyName](value) {
this.element.innerHTML = value;
}
}
9.7 Generator Methods
Naive example
1
2
3
4
5
6
7
8
9class MyClass {
*createIterator() {
yield 1;
yield 2;
yield 3;
}
}
let instance = new MyClass();
let iterator = instance.createIterator();defining a default iterator for your class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Collection {
constructor() {
this.items = [];
}
*[Symbol.iterator]() {
yield *this.items.values();
}
}
var collection = new Collection();
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);
for (let x of collection) {
console.log(x);
}
9.8 Static Members
-
1
2
3
4
5class PersonClass {
static create(name) {
return new PersonClass(name);
}
}
9.9 Inheritance with Derived Classes
9.9.1 Inheritance with Derived Classes
- If specifying a constructor:
super()
is mandatory - If no constructor,
super()
is automatically called with all arguments upon creating a new instance of the class. - Can only use
super()
in a derived class. - Must call
super()
before accessingthis
in the constructor The only way to avoid calling
super()
is to return an object from the class constructor.1
2
3
4
5
6
7
8
9class Square extends Rectangle {
// no constructor
}
// Is equivalent to
class Square extends Rectangle {
constructor(...args) {
super(...args);
}
}
9.9.2 Shadowing Class Method
Just like other languages
1
2
3
4
5
6
7
8
9class Square extends Rectangle {
constructor(length) {
super(length, length);
}
// override and shadow Rectangle.prototype.getArea()
getArea() {
return this.length * this.length;
}
}
9.9.3 Inherited Static Members
Just like other languages
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
getArea() {
return this.length * this.width;
}
static create(length, width) {
return new Rectangle(length, width);
}
}
class Square extends Rectangle {
constructor(length) {
// same as Rectangle.call(this, length, length)
super(length, length);
}
}
var rect = Square.create(3, 4);
console.log(rect instanceof Rectangle); // true
console.log(rect.getArea()); // 12
console.log(rect instanceof Square); // false
9.9.4 Derived Classes from Expressions
- Special
- You can use
extends
with any expression as long as the expression resolves to a function with[[Construct]]
and aprototype
- Use case: dynamic extends
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22function Rectangle(length, width) {
this.length = length;
this.width = width;
}
Rectangle.prototype.getArea = function() {
return this.length * this.width;
};
function getBase() {
return Rectangle;
}
class Square extends getBase() {
constructor(length) {
super(length, length);
}
}
var x = new Square(3);
console.log(x.getArea()); // 9
console.log(x instanceof Rectangle); // true
- You can use
9.9.5 Inheriting from Built-ins
Just like other languages
1
2
3
4
5
6
7
8
9
10class MyArray extends Array {
// empty
}
var colors = new MyArray();
colors[0] = "red";
console.log(colors.length); // 1
colors.length = 0;
console.log(colors[0]); // undefined
9.9.6 The Symbol.species Property
Why?
They think subitems should not be
instanceof MyArray
, while I feel it’s good1
2
3
4
5
6
7
8
9class MyArray extends Array {
// empty
}
let items = new MyArray(1, 2, 3, 4),
subitems = items.slice(1, 3);
console.log(items instanceof MyArray); // true
console.log(subitems instanceof MyArray); // true
The Symbol.species well-known symbol is used to define a static accessor property that returns a function. The following builtin types have Symbol.species defined:
- Array
- ArrayBuffer (discussed in Chapter 10)
- Map
- Promise
- RegExp
- Set
- Typed Arrays (discussed in Chapter 10)
9.10 Using new.target in Class Constructors
- determine how the class is being invoked
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class Shape {
constructor() {
if (new.target === Shape) {
throw new Error("This class cannot be instantiated directly.")
}
}
}
class Rectangle extends Shape {
constructor(length, width) {
super();
this.length = length;
this.width = width;
}
}
var x = new Shape(); // throws error
var y = new Rectangle(3, 4); // no error
console.log(y instanceof Shape); // true
Chapter10 Improved Array Capabilities
Creating Arrays
Array.of()
1
2
3Array.of(1, 2); //[1, 2]
Array.of(1); //[1]
Array.of("1"); //["1"]Array.from(iterable, map, this)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15let helper = {
diff: 1,
add(value) {
return value + this.diff;
}
};
function translate() {
return Array.from(arguments, helper.add, helper);
}
let numbers = translate(1, 2, 3);
console.log(numbers); // 2,3,4
New Methods on All Arrays
- find
find(callback, this)
findIndex(callback, this)
fill(value, startIndex, endIndex)
copyWithin(startFillIndex, startCopyIndex, howMany)
- find
- Typed Arrays
- Improve calculation speed
- Skip
Chapter11 Promises and Asynchronous Programming
11.1 Asynchronous Programming Background
- Background
- JavaScript engines are built on the concept of a single-threaded event loop
- Whenever a piece of code is ready to be executed, it is added to the job queue
- job execution runs from the first job in the queue to the last
- Old Patterns
- The Event Model
- chaining multiple separate asynchronous calls together is more complicated
- button were clicked before onclick is assigned
- The Callback Pattern
- nested callback is dizaster
- asynchronous operations to run in parallel
- The Event Model
11.2 Promise Basics
[[PromiseState]]
: not exposed- Fulfilled: Settled. Completed successfully.
- Rejected: Settled. Didn’t complete successfully
- Pending: Unsettled
Promise vs thenable
- all promises are thenable
- not all thenables are promise
- all thenable has a
then(fulFill, reject)
all promise has a
then(fulFill, reject)
andcatch(reject)
method1
2
3
4
5
6
7
8
9
10
11
12
13let promise = readFile("example.txt");
promise.then(function(contents) {
// fulfillment
console.log(contents);
}, function(err) {
// rejection
console.error(err.message);
});
// same
promise.then(null, reject);
promise.catch(reject);Each call to
then()
orcatch()
creates a new job to be executed when the promise is resolved- Without a rejection handler to a promise, all failures will happen silently
Creating Unsettled Promises
new Promise(excutor)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16let fs = require("fs");
function readFile(filename) {
return new Promise(function(resolve, reject) {
// trigger the asynchronous operation
fs.readFile(filename, { encoding: "utf8" }, function(err, contents) {
// check for errors
if (err) {
reject(err);
return;
}
// the read succeeded
resolve(contents);
});
});
}fulfillment and rejection handlers are always added to the end of the job queue after the executor has completed. Behave like setTimeout
example
1
2
3
4
5
6
7
8
9
10
11
12
13
14let promise = new Promise(function(resolve, reject) {
console.log("Promise");
resolve();
});
promise.then(function() {
console.log("Resolved.");
});
console.log("Hi!");
// Promise
// Hi!
// Resolved
11.3 Global Promise Rejection Handling
- Issue: silent failure that occurs when a promise is rejected without a rejection handler
Solution: Event + setInterval
- event
- unhandlerejection
- rejectionhandled
- setInterval
example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15let possiblyUnhandledRejections = new Map();
window.onunhandledrejection = function(event) {
possiblyUnhandledRejections.set(event.promise, event.reason);
};
window.onrejectionhandled = function(event) {
possiblyUnhandledRejections.delete(event.promise);
};
setInterval(function() {
possiblyUnhandledRejections.forEach(function(reason, promise) {
handleRejection(promise, reason);
});
possiblyUnhandledRejections.clear();
}, 60000);
- event
11.4 Chaining Promises
- Each call to then() or catch() actually creates and returns another promise
- This second promise is resolved only once the first has been fulfilled or rejected
example
1
2
3
4
5
6
7
8
9
10let p1 = new Promise(function(resolve, reject) {resolve(42);});
p1.then(function(value) {
console.log(value);
}).then(function() {
console.log("then");
throw new Error("Boom");
}).catch(function(error) {
console.log(error.message);
});Returning Values in Promise Chains
data
1
2
3
4
5
6
7
8
9
10let p1 = new Promise(function(resolve, reject) {
reject(42);
});
p1.catch(function(value) {
console.log(value); // "42"
return value + 1;
}).then(function(value) {
console.log(value); // "43"
});Promise
1
2
3
4
5
6
7
8
9
10
11
12
13let p1 = new Promise(function(resolve, reject) {
reject(42);
});
p1.catch(function(value) {
console.log(value); // "42"
let p2 = new Promise(function(resolve, reject) {
reject(43);
});
return p2
}).then(function(value) {
console.log(value); // "43"
});
11.5 Responding to Multiple Promises
Promise.all()
- The returned promise is fulfilled when every promise in the iterable is fulfilled
- If anyone is rejected, it is immediately rejected without waiting for others
Promise.race()
- returns as soon as any promise in the array is fulfilled
- if the first promise to settle is rejected, then the returned promise is rejected
11.6 Inheriting from Promises
- Since static methods are inherited, resolve(), reject(), race(), all() method are also present on derived promises
Because of
Symbol.species
, resolve(), reject() will tag class as the derived class1
2
3
4
5
6
7
8
9
10let p1 = new Promise(function(resolve, reject) {
resolve(42);
});
let p2 = MyPromise.resolve(p1);
p2.success(function(value) {
console.log(value); // 42
});
console.log(p2 instanceof MyPromise); // true
Chapter12 Proxies and the Reflection API
- Proxies exposes the inner workings of objects through
- Proxies can intercept and alter low-level operations of the JavaScript engine.
12.1 The Array Problem
- User cannot do
arr.length = 2
12.2 What are Proxies and Reflection?
Proxy Trap | Overrides the Behavior Of | Default Behavior |
---|---|---|
get | Reading a property value | Reflect.get() |
set | Writing to a property | Reflect.set() |
has | The in operator | Reflect.has() |
deleteProperty | The delete operator | Reflect.deleteProperty() |
getPrototypeOf | Object.getPrototypeOf() | Reflect.getPrototypeOf() |
setPrototypeOf | Object.setPrototypeOf() | Reflect.setPrototypeOf() |
isExtensible | Object.isExtensible() | Reflect.isExtensible() |
preventExtensions | Object.preventExtensions() | Reflect.preventExtensions() |
getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor() | Reflect.getOwnPropertyDescriptor() |
defineProperty | Object.defineProperty() | Reflect.defineProperty |
ownKeys | Object.keys, Object.getOwnPropertyNames(), Object.getOwnPropertySymbols() | Reflect.ownKeys() |
apply | Calling a function | Reflect.apply() |
construct | Calling a function with new | Reflect.construct() |
example
set: only allow numbers for new property
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
31let target = {
name: "target"
};
let proxy = new Proxy(target, {
set(trapTarget, key, value, receiver) {
// ignore existing properties so as not to affect them
if (!trapTarget.hasOwnProperty(key)) {
if (isNaN(value)) {
throw new TypeError("Property must be a number.");
}
}
// add the property
return Reflect.set(trapTarget, key, value, receiver);
}
});
// adding a new property
proxy.count = 1;
console.log(proxy.count); // 1
console.log(target.count); // 1
// you can assign to name because it exists on target already
proxy.name = "proxy";
console.log(proxy.name); // "proxy"
console.log(target.name); // "proxy"
// throws an error
proxy.anotherName = "proxy";Revocable Proxies
1
2
3
4
5
6
7
8
9
10
11
12let target = {
name: "target"
};
let { proxy, revoke } = Proxy.revocable(target, {});
console.log(proxy.name); // "target"
revoke();
// throws error
console.log(proxy.name);
Chapter13 Encapsulating Code With Modules
13.1 What are Modules?
- Variables created in the top level of a module exist only within the top-level scope of the module.
- The value of this in the top level of a module is undefined.
- Modules must export anything that should be available to code outside of the module.
- Modules may import bindings from other modules.
13.2 Basic Exporting
- any variables, functions, or classes that are not explicitly exported remain private to the module
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// export data
export var color = "red";
export let name = "Nicholas";
export const magicNumber = 7;
// export function
export function sum(num1, num2) {
return num1 + num1;
}
// export class
export class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
}
13.3 Basic Importing
- An important limitation of both export and import is that they must be used outside other statements and functions
-
1
2
3import { identifier1, identifier2 } from "./example.js";
import { sum } from "./example.js";
import * as example from "./example.js";
13.4 Renaming Exports and Imports
-
1
2
3
4
5function sum(num1, num2) {
return num1 + num2;
}
export { sum as add }; -
1
import { add as sum } from "./example.js";
13.5 Default Values in Modules
-
1
2
3export default function(num1, num2) {
return num1 + num2;
} -
1
2import sum from "./example.js";
import sum, {color} from "./example.js";
13.6 Re-exporting a Binding
-
1
2
3
4import { sum } from "./example.js";
export { sum };
// same as
export { sum } from "./example.js";
13.7 Importing Without Bindings
- Some modules may only make modifications to objects in the global scope
-
1
2
3
4
5
6
7
8
9
10
11// module code without exports or imports
Array.prototype.pushAll = function(items) {
// items must be an array
if (!Array.isArray(items)) {
throw new TypeError("Argument must be an array.");
}
// use built-in push() and spread operator
return this.push(...items);
};
13.8 Loading Modules
-
1
<script type="module" src="module.js"></script>
To support that functionality, always acts as if the defer attribute is applied
- Modules are also executed in the order in which they appear in the HTML file
- async
- All resources the module needs will be downloaded before the module executes
- can’t guarantee when the module will execute
- Loading Modules as Workers
let worker = new Worker("module.js", { type: "module" });
- Browser Module Specifier Resolution
1
2
3
4
5
6
7
8
9
10
11
12
13// this script locates https://www.example.com/modules/module.js
// imports from https://www.example.com/modules/example1.js
import { first } from "./example1.js";
// imports from https://www.example.com/example2.js
import { second } from "../example2.js";
// imports from https://www.example.com/example3.js
import { third } from "/example3.js";
// imports from https://www2.example.com/example4.js
import { fourth } from "https://www2.example.com/example4.js";