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: chapters/ch01.asciidoc
+17-7Lines changed: 17 additions & 7 deletions
Original file line number
Diff line number
Diff line change
@@ -1,7 +1,7 @@
1
1
[[ecmascript-and-the-future-of-javascript]]
2
2
== ECMAScript and the Future of JavaScript
3
3
4
-
JavaScript has gone from being a 1995 marketing ploy to gain a tactical advantage, to becoming the core programming experience in the world's most widely used application runtime platform in 2017. The language doesn't merely run in browsers anymore, but is also used to create desktop and mobile applications, in hardware devices, and even in the vacuum of space.
4
+
JavaScript has gone from being a 1995 marketing ploy to gain a tactical advantage, to becoming the core programming experience in the world's most widely used application runtime platform in 2017. The language doesn't merely run in browsers anymore, but is also used to create desktop and mobile applications, in hardware devices, and even in space suit design at NASA.
5
5
6
6
How did JavaScript get here, and where is it going next?
7
7
@@ -47,7 +47,7 @@ Having spent ten years without observing significant change to the language spec
47
47
48
48
Since ES6 came out, TC39 has streamlinedfootnote:[You can find the September 2013 presentation which lead to the streamlined proposal revisioning process here: https://mjavascript.com/out/tc39-improvement.] its proposal revisioning process and adjusted it to meet modern expectations: the need to iterate more often and consistently, and to democratize specification development. At this point, TC39 moved from an ancient Word-based flow to using ecmarkup and GitHub Pull Requests, greatly increasing the number of proposalsfootnoteref:[proposals,You can find all proposals being considered by TC39 at https://mjavascript.com/out/tc39-proposals.] being created as well as external participation by non-members.
49
49
50
-
Firefox, Chrome, Edge, Safari and Node.js all offer over 95% compliancy of the ES6 specification,footnote:[For a detailed ES6 compatibility report across browsers, check out the following table: https://mjavascript.com/out/es6-compat.] but we’ve been able to use the features as they came out in each of these browsers rather than having to wait until the flip of a switch when their implementation of ES6 was 100% finalized.
50
+
Firefox, Chrome, Edge, Safari and Node.js all offer over 95% compliance of the ES6 specification,footnote:[For a detailed ES6 compatibility report across browsers, check out the following table: https://mjavascript.com/out/es6-compat.] but we’ve been able to use the features as they came out in each of these browsers rather than having to wait until the flip of a switch when their implementation of ES6 was 100% finalized.
51
51
52
52
The new process involves four different maturity stagesfootnote:[The TC39 proposal process documentation can be found at https://mjavascript.com/out/tc39-process.]. The more mature a proposal is, the more likely it is to eventually make it into the specification.
53
53
@@ -160,7 +160,9 @@ For the next step, we'll replace the value of the +scripts+ property in +package
160
160
[source,json]
161
161
----
162
162
{
163
-
"build": "babel src --out-dir dist"
163
+
"scripts": {
164
+
"build": "babel src --out-dir dist"
165
+
}
164
166
}
165
167
----
166
168
@@ -258,12 +260,16 @@ Referencing the +node_modules/.bin+ directory, an implementation detail of how n
258
260
259
261
[source,json]
260
262
----
261
-
"lint": "eslint ."
263
+
{
264
+
"scripts": {
265
+
"lint": "eslint ."
266
+
}
267
+
}
262
268
----
263
269
264
270
As you might recall from the Babel example, +npm+ add +node_modules+ to the +PATH+ when executing scripts. To lint our codebase, we can execute +npm run lint+ and npm will find the ESLint CLI embedded deep in the +node_modules+ directory.
265
271
266
-
Let's consider the following +example.js+ file, which is purposely ridden with style issues, to demonstrate what ESLint does.
272
+
Let's consider the following +example.js+ file, which is purposely riddled with style issues, to demonstrate what ESLint does.
267
273
268
274
[source,javascript]
269
275
----
@@ -283,7 +289,11 @@ ESLint is able to fix most style problems automatically if we pass in a +--fix+
283
289
284
290
[source,json]
285
291
----
286
-
"lint-fix": "eslint . --fix"
292
+
{
293
+
"scripts": {
294
+
"lint-fix": "eslint . --fix"
295
+
}
296
+
}
287
297
----
288
298
289
299
When we run +lint-fix+ we'll only get a pair of errors: +hello+ is never used and +false+ is a constant condition. Every other error has been fixed in place, resulting in the bit of source code found below. The remaining errors weren't fixed because ESLint avoids making assumptions about our code, and prefers not to incur in semantic changes. In doing so, +--fix+ becomes a useful tool to resolve code style wrinkles without risking a broken program as a result.
@@ -317,7 +327,7 @@ We get several new mechanics to describe asynchronous code flows in ES6: promise
317
327
318
328
There's a common practice in JavaScript where developers use plain objects to create hash maps with arbitrary string keys. This can lead to vulnerabilities if we're not careful and let user input end up defining those keys. ES6 introduces a few different native built-ins to manage sets and maps, which don't have the limitation of using string keys exclusively. These collections are explored in chapter 5.
319
329
320
-
Proxy objects redefine what can be done through JavaScript reflection. Proxy objects are similar to proxies in other contexts, such as web traffic routing. They can intercept any interaction with a JavaScript object such as defining, deleting, or accessing a property. Given the mechanics of how proxies work, they are impossible to implement holistically as a polyfill. We'll devote chapter 6 to understanding proxies.
330
+
Proxy objects redefine what can be done through JavaScript reflection. Proxy objects are similar to proxies in other contexts, such as web traffic routing. They can intercept any interaction with a JavaScript object such as defining, deleting, or accessing a property. Given the mechanics of how proxies work, they are impossible to polyfill holistically: polyfills exist, but they have limitations making them incompatible with the specification in some use cases. We'll devote chapter 6 to understanding proxies.
321
331
322
332
Besides new built-ins, ES6 comes with several updates to +Number+, +Math+, +Array+, and strings. In chapter 7 we'll go over a plethora of new instance and static methods added to these built-ins.
Copy file name to clipboardExpand all lines: chapters/ch02.asciidoc
+94-9Lines changed: 94 additions & 9 deletions
Original file line number
Diff line number
Diff line change
@@ -232,7 +232,7 @@ var example = (parameters) => {
232
232
}
233
233
----
234
234
235
-
While arrow functions look very similar to your typical anonymous function, they are fundamentally different: arrow functions can't have a name, theycan't be used as constructors, they don't have a +prototype+ property, and they are bound to their lexical scope.
235
+
While arrow functions look very similar to your typical anonymous function, they are fundamentally different: arrow functions can't be named explicitly, although modern runtimes can infer a name based on the variable they're assigned to; they can't be used as constructors nor do they have a +prototype+ property, meaning you can't use +new+ on an arrow function; and they are bound to their lexical scope, which is the reason why they don't alter the meaning of +this+.
236
236
237
237
Let's dig into their semantic differences with traditional functions, the many ways to declare an arrow function, and practical use cases.
238
238
@@ -261,6 +261,32 @@ If we had defined the function passed to +setInterval+ as a regular anonymous fu
261
261
262
262
In a similar fashion, lexical binding in ES6 arrow functions also means that function calls won't be able to change the +this+ context when using +.call+, +.apply+, +.bind+, etc. That limitation is usually more useful than not, as it ensures that the context will always be preserved and constant.
263
263
264
+
Let's now shift our attention to the following example. What do you think the `console.log` statement will print?
265
+
266
+
[source,javascript]
267
+
----
268
+
function puzzle () {
269
+
return function () {
270
+
console.log(arguments)
271
+
}
272
+
}
273
+
puzzle('a', 'b', 'c')(1, 2, 3)
274
+
----
275
+
276
+
The answer is that `arguments` refers to the context of the anonymous function, and thus the arguments passed to that function will be printed. In this case, those arguments are `1, 2, 3`.
277
+
278
+
What about in the following case, where we use an arrow function instead of the anonymous function in the previous example?
279
+
280
+
[source,javascript]
281
+
----
282
+
function puzzle () {
283
+
return () => console.log(arguments)
284
+
}
285
+
puzzle('a', 'b', 'c')(1, 2, 3)
286
+
----
287
+
288
+
In this case, the `arguments` object refers to the context of the `puzzle` function, because arrow functions don't create a closure. For this reason, the printed arguments will be `'a', 'b', 'c'`.
289
+
264
290
I've mentioned there's several flavors of arrow functions, but so far we've only looked at their fully fleshed version. What are the others way to represent an arrow function?
265
291
266
292
==== 2.2.2 Arrow Function Flavors
@@ -274,7 +300,7 @@ var example = (parameters) => {
274
300
}
275
301
----
276
302
277
-
An arrow function with exactly one parameter can omit the parenthesis. This is optional. It's useful when passing the arrow function to another method, as it reduces the amount of parenthesis involved, making it easier for humans to parse the code.
303
+
An arrow function with exactly one parameter can omit the parenthesis. This is optional. It's useful when passing the arrow function to another method, as it reduces the amount of parenthesis involved, making it easier for some humans to parse the code.
278
304
279
305
[source,javascript]
280
306
----
@@ -738,7 +764,7 @@ Let's turn our attention to spread and rest operators next.
738
764
739
765
=== 2.4 Rest Parameters and Spread Operator
740
766
741
-
Before ES6, interacting with an arbitrary amount of function parameters was complicated. You had to use +arguments+, which isn't an array but has a +length+ property. Usually you'd end up casting the +arguments+ object into an actual array using +Array.prototype.slice.call+, and going from there, as shown in the following snippet.
767
+
Before ES6, interacting with an arbitrary amount of function parameters was complicated. You had to use +arguments+, which isn't an array but has a +length+ property. Usually you'd end up casting the +arguments+ object into an actual array using +Array#slice.call+, and going from there, as shown in the following snippet.
742
768
743
769
[source,javascript]
744
770
----
@@ -861,8 +887,8 @@ In ES6, you can combine spread with array destructuring. The following piece of
861
887
862
888
[source,javascript]
863
889
----
864
-
var [first, second, ...rest] = ['a', 'b', 'c', 'd', 'e']
865
-
console.log(rest)
890
+
var [first, second, ...other] = ['a', 'b', 'c', 'd', 'e']
891
+
console.log(other)
866
892
// <- ['c', 'd', 'e']
867
893
----
868
894
@@ -921,8 +947,8 @@ The following table summarizes the use cases we've discussed for the spread oper
|+new+ and +apply+|+new (Date.bind.apply(Date, [null,2015,31,8]))+| +new Date(...[2015,31,8])+
927
953
|=======
928
954
@@ -1062,7 +1088,37 @@ The template we've just prepared would produce output like what's shown in the f
1062
1088
</article>
1063
1089
----
1064
1090
1065
-
Sometimes, it might be a good idea to pre-process the results of expressions before inserting them into your templates. For these advanced kinds of use cases, it's possible to use another feature of template literals called tagged templates.
1091
+
A downside when it comes to multi-line template literals is indentation. The following example shows a typically indented piece of code with a template literal contained in a function. While we may have expected no indentation, the string is has four spaces of indentation.
1092
+
1093
+
[source,javascript]
1094
+
----
1095
+
function getParagraph () {
1096
+
return `
1097
+
Dear Rod,
1098
+
1099
+
This is a template literal string that's indented
1100
+
four spaces. However, you may have expected for it
1101
+
to be not indented at all.
1102
+
1103
+
Nico
1104
+
`
1105
+
}
1106
+
----
1107
+
1108
+
While not ideal, we could get away with a utility function to remove indentation from each line in the resulting string.
1109
+
1110
+
[source,javascript]
1111
+
----
1112
+
function unindent (text) {
1113
+
return text
1114
+
.split('\n')
1115
+
.map(line => line.slice(4))
1116
+
.join('\n')
1117
+
.trim()
1118
+
}
1119
+
----
1120
+
1121
+
Sometimes, it might be a good idea to pre-process the results of interpolated expressions before inserting them into your templates. For these advanced kinds of use cases, it's possible to use another feature of template literals called tagged templates.
1066
1122
1067
1123
==== 2.5.3 Tagged Templates
1068
1124
@@ -1126,7 +1182,7 @@ console.log(text)
1126
1182
// <- 'Hello MAURICE, I am THRILLED to meet you!'
1127
1183
----
1128
1184
1129
-
A decidedly more useful use case would be to sanitize expressions interpolated into your templates, automatically, using a tagged template. Given a template where all expressions are considered user-input, we could use a hypothetical +sanitize+ library to remove HTML tags and similar hazards.
1185
+
A decidedly more useful use case would be to sanitize expressions interpolated into your templates, automatically, using a tagged template. Given a template where all expressions are considered user-input, we could use a hypothetical +sanitize+ library to remove HTML tags and similar hazards, preventing cross site scripting (XSS) attacks where users might inject malicious HTML into our websites.
1130
1186
1131
1187
[source,javascript]
1132
1188
----
@@ -1225,6 +1281,35 @@ console.log(i)
1225
1281
// <- i is not defined
1226
1282
----
1227
1283
1284
+
Given +let+ variables declared in a loop are scoped to each step in the loop, the bindings would work as expected in combination with an asynchronous function call, as opposed to what we're used to with +var+. Let's look at concrete examples.
1285
+
1286
+
First, we'll look at the typical example of how +var+ scoping works. The +i+ binding is scoped to the +printNumbers+ function, and its value increases all the way to +10+ as each timeout callback is scheduled. By the time each callbacks run -- one every 100 milliseconds -- +i+ has a value of +10+ and thus that's what's printed every single time.
1287
+
1288
+
[source,javascript]
1289
+
----
1290
+
function printNumbers () {
1291
+
for (var i = 0; i < 10; i++) {
1292
+
setTimeout(function () {
1293
+
console.log(i)
1294
+
}, i * 100)
1295
+
}
1296
+
}
1297
+
printNumbers()
1298
+
----
1299
+
1300
+
Using +let+, in contrast, binds the variable to the block's scope. Indeed, each step in the loop still increases the value of the variable, but a new binding is created each step of the way, meaning that each timeout callback will hold a reference to the binding holding the value of +i+ at the point when the callback was scheduled, printing every number from +0+ through +9+ as expected.
1301
+
1302
+
[source,javascript]
1303
+
----
1304
+
function printNumbers () {
1305
+
for (let i = 0; i < 10; i++) {
1306
+
setTimeout(function () {
1307
+
console.log(i)
1308
+
}, i * 100)
1309
+
}
1310
+
}
1311
+
printNumbers()
1312
+
----
1228
1313
1229
1314
One more thing of note about +let+ is a concept called the "Temporal Dead Zone".
0 commit comments