Statistics
10
Views
0
Downloads
0
Donations
Support
Share
Uploader

高宏飞

Shared on 2026-01-09

AuthorKyle Simpson

No description

Tags
No tags
Publish Year: 2022
Language: 英文
File Format: PDF
File Size: 2.2 MB
Support Statistics
¥.00 · 0times
Text Preview (First 20 pages)
Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

(This page has no text content)
You Don't Know JS: ES6 & Beyond Kyle Simpson
Foreword Kyle Simpson is a thorough pragmatist. I can’t think of higher praise than this. To me, these are two of the most important qualities that a software developer must have. That’s right: must, not should. Kyle’s keen ability to tease apart layers of the JavaScript programming language and present them in understandable and meaningful portions is second to none. ES6 & Beyond will be familiar to readers of the You Don’t Know JS series: they can expect to be deeply immersed in everything from the obvious, to the very subtle – revealing semantics that were either taken for granted or never even considered. Until now, the You Don’t Know JS book series has covered material that has at least some degree of familiarity to its readers. They have either seen or heard about the subject matter; they may even have experience with it. This volume covers material that only a very small portion of the JavaScript developer community has been exposed to: the evolutionary changes to the language introduced in the ECMAScript 2015 Language Specification. Over the last couple years, I’ve witnessed Kyle’s tireless efforts to familiarize himself with this material to a level of expertise that is rivaled by only a handful of his professional peers. That’s quite a feat, considering that at the time of this writing, the language specification document hasn’t been formally published! But what I’ve said is true, and I’ve read every word that Kyle’s written for this book. I’ve followed every change, and each time, the content only gets better and provides yet a deeper level of understanding.
This book is about shaking up your sense of understanding by exposing you to the new and unknown. The intention is to evolve your knowledge in step with your tools by bestowing you with new capabilities. It exists to give you the confidence to fully embrace the next major era of JavaScript programming. Rick Waldron [@rwaldron](http://twitter.com/rwaldron) Open Web Engineer at Bocoup Ecma/TC39 Representative for jQuery
Preface I’m sure you noticed, but “JS” in the book series title is not an abbreviation for words used to curse about JavaScript, though cursing at the language’s quirks is something we can probably all identify with! From the earliest days of the web, JavaScript has been a foundational technology that drives interactive experience around the content we consume. While flickering mouse trails and annoying pop-up prompts may be where JavaScript started, nearly 2 decades later, the technology and capability of JavaScript has grown many orders of magnitude, and few doubt its importance at the heart of the world’s most widely available software platform: the web. But as a language, it has perpetually been a target for a great deal of criticism, owing partly to its heritage but even more to its design philosophy. Even the name evokes, as Brendan Eich once put it, “dumb kid brother” status next to its more mature older brother “Java”. But the name is merely an accident of politics and marketing. The two languages are vastly different in many important ways. “JavaScript” is as related to “Java” as “Carnival” is to “Car”. Because JavaScript borrows concepts and syntax idioms from several languages, including proud C-style procedural roots as well as subtle, less obvious Scheme/Lisp-style functional roots, it is exceedingly approachable to a broad audience of developers, even those with just little to no programming experience. The “Hello World” of JavaScript is so simple that the language is inviting and easy to get comfortable with in early exposure.
While JavaScript is perhaps one of the easiest languages to get up and running with, its eccentricities make solid mastery of the language a vastly less common occurrence than in many other languages. Where it takes a pretty in-depth knowledge of a language like C or C++ to write a full-scale program, full-scale production JavaScript can, and often does, barely scratch the surface of what the language can do. Sophisticated concepts which are deeply rooted into the language tend instead to surface themselves in seemingly simplistic ways, such as passing around functions as callbacks, which encourages the JavaScript developer to just use the language as-is and not worry too much about what’s going on under the hood. It is simultaneously a simple, easy-to-use language that has broad appeal, and a complex and nuanced collection of language mechanics which without careful study will elude true understanding even for the most seasoned of JavaScript developers. Therein lies the paradox of JavaScript, the Achilles’ Heel of the language, the challenge we are presently addressing. Because JavaScript can be used without understanding, the understanding of the language is often never attained. Mission If at every point that you encounter a surprise or frustration in JavaScript, your response is to add it to the blacklist, as some are accustomed to doing, you soon will be relegated to a hollow shell of the richness of JavaScript.
While this subset has been famously dubbed “The Good Parts”, I would implore you, dear reader, to instead consider it the “The Easy Parts”, “The Safe Parts”, or even “The Incomplete Parts”. This You Don’t Know JavaScript book series offers a contrary challenge: learn and deeply understand all of JavaScript, even and especially “The Tough Parts”. Here, we address head on the tendency of JS developers to learn “just enough” to get by, without ever forcing themselves to learn exactly how and why the language behaves the way it does. Furthermore, we eschew the common advice to retreat when the road gets rough. I am not content, nor should you be, at stopping once something just works, and not really knowing why. I gently challenge you to journey down that bumpy “road less traveled” and embrace all that JavaScript is and can do. With that knowledge, no technique, no framework, no popular buzzword acronym of the week, will be beyond your understanding. These books each take on specific core parts of the language which are most commonly misunderstood or under-understood, and dive very deep and exhaustively into them. You should come away from reading with a firm confidence in your understanding, not just of the theoretical, but the practical “what you need to know” bits. The JavaScript you know right now is probably parts handed down to you by others who’ve been burned by incomplete understanding. That JavaScript is but a shadow of the true language. You don’t really know JavaScript, yet, but if you dig into this series, you will. Read on, my friends. JavaScript awaits you.
Summary JavaScript is awesome. It’s easy to learn partially, and much harder to learn completely (or even sufficiently). When developers encounter confusion, they usually blame the language instead of their lack of understanding. These books aim to fix that, inspiring a strong appreciation for the language you can now, and should, deeply know. Note: Many of the examples in this book assume modern (and future-reaching) JavaScript engine environments, such as ES6. Some code may not work as described if run in older (pre-ES6) engines.
Chapter 1: ES? Now & Future Before you dive into this book, you should have a solid working proficiency over JavaScript up to the most recent standard (at the time of this writing), which is commonly called ES5 (technically ES 5.1). Here, we plan to talk squarely about the upcoming ES6, as well as cast our vision beyond to understand how JS will evolve moving forward. If you are still looking for confidence with JavaScript, I highly recommend you read the other titles in this series first: Up & Going: Are you new to programming and JS? This is the roadmap you need to consult as you start your learning journey. Scope & Closures: Did you know that JS lexical scope is based on compiler (not interpreter!) semantics? Can you explain how closures are a direct result of lexical scope and functions as values? this & Object Prototypes: Can you recite the four simple rules for how this is bound? Have you been muddling through fake “classes” in JS instead of adopting the simpler “behavior delegation” design pattern? Ever heard of objects linked to other objects (OLOO)? Types & Grammar: Do you know the built-in types in JS, and more importantly, do you know how to properly and safely use coercion between types? How comfortable are you with the nuances of JS grammar/syntax? Async & Performance: Are you still using callbacks to manage your asynchrony? Can you explain what a promise is and
why/how it solves “callback hell”? Do you know how to use generators to improve the legibility of async code? What exactly constitutes mature optimization of JS programs and individual operations? If you’ve already read all those titles and you feel pretty comfortable with the topics they cover, it’s time we dive into the evolution of JS to explore all the changes coming not only soon but farther over the horizon. Unlike ES5, ES6 is not just a modest set of new APIs added to the language. It incorporates a whole slew of new syntactic forms, some of which may take quite a bit of getting used to. There’s also a variety of new organization forms and new API helpers for various data types. ES6 is a radical jump forward for the language. Even if you think you know JS in ES5, ES6 is full of new stuff you don’t know yet, so get ready! This book explores all the major themes of ES6 that you need to get up to speed on, and even gives you a glimpse of future features coming down the track that you should be aware of. Warning: All code in this book assumes an ES6+ environment. At the time of this writing, ES6 support varies quite a bit in browsers and JS environments (like Node.js), so your mileage may vary. Versioning The JavaScript standard is referred to officially as “ECMAScript” (abbreviated “ES”), and up until just recently has been versioned entirely by ordinal number (i.e., “5” for “5th edition”).
The earliest versions, ES1 and ES2, were not widely known or implemented. ES3 was the first widespread baseline for JavaScript, and constitutes the JavaScript standard for browsers like IE6-8 and older Android 2.x mobile browsers. For political reasons beyond what we’ll cover here, the ill-fated ES4 never came about. In 2009, ES5 was officially finalized (later ES5.1 in 2011), and settled as the widespread standard for JS for the modern revolution and explosion of browsers, such as Firefox, Chrome, Opera, Safari, and many others. Leading up to the expected next version of JS (slipped from 2013 to 2014 and then 2015), the obvious and common label in discourse has been ES6. However, late into the ES6 specification timeline, suggestions have surfaced that versioning may in the future switch to a year-based schema, such as ES2016 (aka ES7) to refer to whatever version of the specification is finalized before the end of 2016. Some disagree, but ES6 will likely maintain its dominant mindshare over the late- change substitute ES2015. However, ES2016 may in fact signal the new year-based schema. It has also been observed that the pace of JS evolution is much faster even than single-year versioning. As soon as an idea begins to progress through standards discussions, browsers start prototyping the feature, and early adopters start experimenting with the code. Usually well before there’s an official stamp of approval, a feature is de facto standardized by virtue of this early engine/tooling prototyping. So it’s also valid to consider the future of JS versioning
to be per-feature rather than per-arbitrary-collection-of-major- features (as it is now) or even per-year (as it may become). The takeaway is that the version labels stop being as important, and JavaScript starts to be seen more as an evergreen, living standard. The best way to cope with this is to stop thinking about your code base as being “ES6-based,” for instance, and instead consider it feature by feature for support. Transpiling Made even worse by the rapid evolution of features, a problem arises for JS developers who at once may both strongly desire to use new features while at the same time being slapped with the reality that their sites/apps may need to support older browsers without such support. The way ES5 appears to have played out in the broader industry, the typical mindset was that code bases waited to adopt ES5 until most if not all pre-ES5 environments had fallen out of their support spectrum. As a result, many are just recently (at the time of this writing) starting to adopt things like strict mode, which landed in ES5 over five years ago. It’s widely considered to be a harmful approach for the future of the JS ecosystem to wait around and trail the specification by so many years. All those responsible for evolving the language desire for developers to begin basing their code on the new features and patterns as soon as they stabilize in specification form and browsers have a chance to implement them.
So how do we resolve this seeming contradiction? The answer is tooling, specifically a technique called transpiling (transformation + compiling). Roughly, the idea is to use a special tool to transform your ES6 code into equivalent (or close!) matches that work in ES5 environments. For example, consider shorthand property definitions (see “Object Literal Extensions” in Chapter 2). Here’s the ES6 form: var foo = [1,2,3]; var obj = { foo // means `foo: foo` }; obj.foo; // [1,2,3] But (roughly) here’s how that transpiles: var foo = [1,2,3]; var obj = { foo: foo }; obj.foo; // [1,2,3] This is a minor but pleasant transformation that lets us shorten the foo: foo in an object literal declaration to just foo, if the names are the same. Transpilers perform these transformations for you, usually in a build workflow step similar to how you perform linting, minification, and other similar operations.
Shims/Polyfills Not all new ES6 features need a transpiler. Polyfills (aka shims) are a pattern for defining equivalent behavior from a newer environment into an older environment, when possible. Syntax cannot be polyfilled, but APIs often can be. For example, Object.is(..) is a new utility for checking strict equality of two values but without the nuanced exceptions that === has for NaN and -0 values. The polyfill for Object.is(..) is pretty easy: if (!Object.is) { Object.is = function(v1, v2) { // test for `-0` if (v1 === 0 && v2 === 0) { return 1 / v1 === 1 / v2; } // test for `NaN` if (v1 !== v1) { return v2 !== v2; } // everything else return v1 === v2; }; } Tip: Pay attention to the outer if statement guard wrapped around the polyfill. This is an important detail, which means the snippet only defines its fallback behavior for older environments where the API in question isn’t already defined; it would be very rare that you’d want to overwrite an existing API. There’s a great collection of ES6 shims called “ES6 Shim” (https://github.com/paulmillr/es6-shim/) that you should definitely
adopt as a standard part of any new JS project! It is assumed that JS will continue to evolve constantly, with browsers rolling out support for features continually rather than in large chunks. So the best strategy for keeping updated as it evolves is to just introduce polyfill shims into your code base, and a transpiler step into your build workflow, right now and get used to that new reality. If you decide to keep the status quo and just wait around for all browsers without a feature supported to go away before you start using the feature, you’re always going to be way behind. You’ll sadly be missing out on all the innovations designed to make writing JavaScript more effective, efficient, and robust. Review ES6 (some may try to call it ES2015) is just landing as of the time of this writing, and it has lots of new stuff you need to learn! But it’s even more important to shift your mindset to align with the new way that JavaScript is going to evolve. It’s not just waiting around for years for some official document to get a vote of approval, as many have done in the past. Now, JavaScript features land in browsers as they become ready, and it’s up to you whether you’ll get on the train early or whether you’ll be playing costly catch-up games years from now. Whatever labels that future JavaScript adopts, it’s going to move a lot quicker than it ever has before. Transpilers and shims/polyfills are
important tools to keep you on the forefront of where the language is headed. If there’s any narrative important to understand about the new reality for JavaScript, it’s that all JS developers are strongly implored to move from the trailing edge of the curve to the leading edge. And learning ES6 is where that all starts!
Chapter 2: Syntax If you’ve been writing JS for any length of time, odds are the syntax is pretty familiar to you. There are certainly many quirks, but overall it’s a fairly reasonable and straightforward syntax that draws many similarities from other languages. However, ES6 adds quite a few new syntactic forms that take some getting used to. In this chapter, we’ll tour through them to find out what’s in store. Tip: At the time of this writing, some of the features discussed in this book have been implemented in various browsers (Firefox, Chrome, etc.), but some have only been partially implemented and many others have not been implemented at all. Your experience may be mixed trying these examples directly. If so, try them out with transpilers, as most of these features are covered by those tools. ES6Fiddle (http://www.es6fiddle.net/) is a great, easy-to-use playground for trying out ES6, as is the online REPL for the Babel transpiler (http://babeljs.io/repl/). Block-Scoped Declarations You’re probably aware that the fundamental unit of variable scoping in JavaScript has always been the function. If you needed to create a block of scope, the most prevalent way to do so other than a regular function declaration was the immediately invoked function expression (IIFE). For example:
var a = 2; (function IIFE(){ var a = 3; console.log( a ); // 3 })(); console.log( a ); // 2 let Declarations However, we can now create declarations that are bound to any block, called (unsurprisingly) block scoping. This means all we need is a pair of { .. } to create a scope. Instead of using var, which always declares variables attached to the enclosing function (or global, if top level) scope, use let: var a = 2; { let a = 3; console.log( a ); // 3 } console.log( a ); // 2 It’s not very common or idiomatic thus far in JS to use a standalone { .. } block, but it’s always been valid. And developers from other languages that have block scoping will readily recognize that pattern. I believe this is the best way to create block-scoped variables, with a dedicated { .. } block. Moreover, you should always put the let declaration(s) at the very top of that block. If you have more than one to declare, I’d recommend using just one let.
Stylistically, I even prefer to put the let on the same line as the opening {, to make it clearer that this block is only for the purpose of declaring the scope for those variables. { let a = 2, b, c; // .. } Now, that’s going to look strange and it’s not likely going to match the recommendations given in most other ES6 literature. But I have reasons for my madness. There’s another experimental (not standardized) form of the let declaration called the let-block, which looks like: let (a = 2, b, c) { // .. } That form is what I call explicit block scoping, whereas the let .. declaration form that mirrors var is more implicit, as it kind of hijacks whatever { .. } pair it’s found in. Generally developers find explicit mechanisms a bit more preferable than implicit mechanisms, and I claim this is one of those cases. If you compare the previous two snippet forms, they’re very similar, and in my opinion both qualify stylistically as explicit block scoping. Unfortunately, the let (..) { .. } form, the most explicit of the options, was not adopted in ES6. That may be revisited post-ES6, but for now the former option is our best bet, I think. To reinforce the implicit nature of let .. declarations, consider these usages:
let a = 2; if (a > 1) { let b = a * 3; console.log( b ); // 6 for (let i = a; i <= b; i++) { let j = i + 10; console.log( j ); } // 12 13 14 15 16 let c = a + b; console.log( c ); // 8 } Quick quiz without looking back at that snippet: which variable(s) exist only inside the if statement, and which variable(s) exist only inside the for loop? The answers: the if statement contains b and c block-scoped variables, and the for loop contains i and j block-scoped variables. Did you have to think about it for a moment? Does it surprise you that i isn’t added to the enclosing if statement scope? That mental pause and questioning – I call it a “mental tax” – comes from the fact that this let mechanism is not only new to us, but it’s also implicit. There’s also hazard in the let c = .. declaration appearing so far down in the scope. Unlike traditional var-declared variables, which are attached to the entire enclosing function scope regardless of where they appear, let declarations attach to the block scope but are not initialized until they appear in the block.