Understanding ECMAScript 6 The Definitive Guide For Javascript Developers 1st Edition Nicholas C. Zakas 2024 Scribd Download
Understanding ECMAScript 6 The Definitive Guide For Javascript Developers 1st Edition Nicholas C. Zakas 2024 Scribd Download
com
https://textbookfull.com/product/understanding-
ecmascript-6-the-definitive-guide-for-javascript-
developers-1st-edition-nicholas-c-zakas/
OR CLICK BUTTON
DOWNLOAD NOW
https://textbookfull.com/product/javascript-the-definitive-guide-7th-
edition-david-flanagan/
textboxfull.com
https://textbookfull.com/product/ios-application-security-the-
definitive-guide-for-hackers-and-developers-1st-edition-david-thiel/
textboxfull.com
The Definitive Guide to AdonisJs: Building Node.js
Applications with JavaScript Christopher Pitt
https://textbookfull.com/product/the-definitive-guide-to-adonisjs-
building-node-js-applications-with-javascript-christopher-pitt/
textboxfull.com
https://textbookfull.com/product/javascript-on-things-hacking-
hardware-for-web-developers-lyza-danger-gardner/
textboxfull.com
https://textbookfull.com/product/understanding-compression-data-
compression-for-modern-developers-1st-edition-colt-mcanlis/
textboxfull.com
T H E F I N E ST I N G E E K E N T E RTA I N M E N T ™
w w w.nostarch.com
ZAKAS
FSC FPO
Understanding ECMASCript 6
Understanding
E C M AS C r i p t 6
The Definitive Guide for
JavaScript Developers
b y Ni ch ol a s C . Za ka s
San Francisco
Understanding ECMASCript 6. Copyright © 2016 by Nicholas C. Zakas.
All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording, or by any information storage or retrieval
system, without the prior written permission of the copyright owner and the publisher.
Printed in USA
First printing
20 19 18 17 16 1 2 3 4 5 6 7 8 9
ISBN-10: 1-59327-757-1
ISBN-13: 978-1-59327-757-4
For information on distribution, translations, or bulk sales, please contact No Starch Press, Inc. directly:
No Starch Press and the No Starch Press logo are registered trademarks of No Starch Press, Inc. Other
product and company names mentioned herein may be the trademarks of their respective owners. Rather
than use a trademark symbol with every occurrence of a trademarked name, we are using the names only
in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the
trademark.
The information in this book is distributed on an “As Is” basis, without warranty. While every precaution
has been taken in the preparation of this work, neither the author nor No Starch Press, Inc. shall have any
liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or
indirectly by the information contained in it.
About the Author
Nicholas C. Zakas has been working on web applications since 2000,
focusing on frontend development, and is known for writing and speak-
ing about frontend best practices. He honed his experience during
his five years at Yahoo!, where he was principal frontend engineer for
the Yahoo! home page. He is the author of several books, including
The Principles of Object-Oriented JavaScript (No Starch Press, 2014) and
Professional JavaScript for Web Developers (Wrox, 2012).
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxi
Chapter 3: Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
Conte nt s in De ta il
acknowledgments xix
Introduction xxi
The Road to ECMAScript 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxi
About This Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxii
Browser and Node.js Compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiii
Who This Book Is For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiii
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiii
Conventions Used . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiv
Help and Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxv
1
Block Bindings 1
var Declarations and Hoisting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Block-Level Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
let Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
No Redeclaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
const Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
The Temporal Dead Zone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Block Bindings in Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Functions in Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
let Declarations in Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
const Declarations in Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Global Block Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Emerging Best Practices for Block Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2
Strings and Regular Expressions 13
Better Unicode Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
UTF-16 Code Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
The codePointAt() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
The String.fromCodePoint() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
The normalize() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
The Regular Expression u Flag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Other String Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Methods for Identifying Substrings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
The repeat() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Other Regular Expression Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
The Regular Expression y Flag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Duplicating Regular Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
The flags Property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Template Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Basic Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Multiline Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Making Substitutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Tagged Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3
Functions 35
Functions with Default Parameter Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Simulating Default Parameter Values in ECMAScript 5 . . . . . . . . . . . . . . . . . . 36
Default Parameter Values in ECMAScript 6 . . . . . . . . . . . . . . . . . . . . . . . . . . 37
How Default Parameter Values Affect the arguments Object . . . . . . . . . . . . . . 38
Default Parameter Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Default Parameter TDZ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Working with Unnamed Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Unnamed Parameters in ECMAScript 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Rest Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Increased Capabilities of the Function Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
The Spread Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
The name Property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Choosing Appropriate Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Special Cases of the name Property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Clarifying the Dual Purpose of Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Determining How a Function Was Called in ECMAScript 5 . . . . . . . . . . . . . . . 50
The new.target Metaproperty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Block-Level Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Deciding When to Use Block-Level Functions . . . . . . . . . . . . . . . . . . . . . . . . . 53
Block-Level Functions in Non-Strict Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Arrow Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Arrow Function Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Creating Immediately Invoked Function Expressions . . . . . . . . . . . . . . . . . . . . 57
No this Binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Arrow Functions and Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
No arguments Binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
Identifying Arrow Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Tail Call Optimization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
How Tail Calls Are Different in ECMAScript 6 . . . . . . . . . . . . . . . . . . . . . . . . 62
How to Harness Tail Call Optimization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4
Expanded Object Functionality 67
Object Categories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Object Literal Syntax Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Property Initializer Shorthand . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Concise Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Computed Property Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
x Contents in Detail
New Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
The Object.is() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
The Object.assign() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Duplicate Object Literal Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Own Property Enumeration Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Enhancements for Prototypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Changing an Object’s Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Easy Prototype Access with Super References . . . . . . . . . . . . . . . . . . . . . . . . 77
A Formal Method Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5
Destructuring for Easier Data Access 83
Why Is Destructuring Useful? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Object Destructuring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Destructuring Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Default Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Assigning to Different Local Variable Names . . . . . . . . . . . . . . . . . . . . . . . . . 87
Nested Object Destructuring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Array Destructuring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Destructuring Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Default Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Nested Array Destructuring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Rest Items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Mixed Destructuring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
Destructured Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Destructured Parameters Are Required . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Default Values for Destructured Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
6
Symbols and Symbol Properties 99
Creating Symbols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Using Symbols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Sharing Symbols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Symbol Coercion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Retrieving Symbol Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Exposing Internal Operations with Well-Known Symbols . . . . . . . . . . . . . . . . . . . . . . 105
The Symbol.hasInstance Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
The Symbol.isConcatSpreadable Property . . . . . . . . . . . . . . . . . . . . . . . . . . 107
The Symbol.match, Symbol.replace, Symbol.search, and
Symbol.split Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
The Symbol.toPrimitive Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
The Symbol.toStringTag Property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
The Symbol.unscopables Property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
Contents in Detail xi
7
Sets and Maps 119
Sets and Maps in ECMAScript 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
Problems with Workarounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Sets in ECMAScript 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Creating Sets and Adding Items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Removing Items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
The forEach() Method for Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Converting a Set to an Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Weak Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
Maps in ECMAScript 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Map Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Map Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
The forEach() Method for Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
Weak Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
8
Iterators and Generators 137
The Loop Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
What Are Iterators? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
What Are Generators? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Generator Function Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Generator Object Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
Iterables and for-of Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
Accessing the Default Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
Creating Iterables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
Built-In Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Collection Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
String Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
NodeList Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
The Spread Operator and Nonarray Iterables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
Advanced Iterator Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Passing Arguments to Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Throwing Errors in Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
Generator Return Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
Delegating Generators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Asynchronous Task Running . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
A Simple Task Runner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
Task Running with Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
An Asynchronous Task Runner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
9
Introducing JavaScript Classes 165
Class-Like Structures in ECMAScript 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
Class Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
A Basic Class Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
Why Use the Class Syntax? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
xii Contents in Detail
Class Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
A Basic Class Expression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
Named Class Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
Classes as First-Class Citizens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
Accessor Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
Computed Member Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
Generator Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
Static Members . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
Inheritance with Derived Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
Shadowing Class Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Inherited Static Members . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
Derived Classes from Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
Inheriting from Built-Ins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
The Symbol.species Property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
Using new.target in Class Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
10
Improved Array Capabilities 191
Creating Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
The Array.of() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
The Array.from() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
New Methods on All Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
The find() and findIndex() Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
The fill() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
The copyWithin() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Typed Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
Numeric Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Array Buffers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Manipulating Array Buffers with Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Similarities Between Typed and Regular Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Common Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
The Same Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
The of() and from() Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Differences Between Typed and Regular Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Behavioral Differences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Missing Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
Additional Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
11
Promises and Asynchronous Programming 213
Asynchronous Programming Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
The Event Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
The Callback Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
Promise Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
The Promise Life Cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Creating Unsettled Promises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Creating Settled Promises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Executor Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
Contents in Detail xiii
Global Promise Rejection Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
Node.js Rejection Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Browser Rejection Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
Chaining Promises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
Catching Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Returning Values in Promise Chains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
Returning Promises in Promise Chains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
Responding to Multiple Promises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
The Promise.all() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
The Promise.race() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Inheriting from Promises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
Promise-Based Asynchronous Task Running . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
12
Proxies and the Reflection API 243
The Array Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
Introducing Proxies and Reflection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
Creating a Simple Proxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
Validating Properties Using the set Trap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
Object Shape Validation Using the get Trap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
Hiding Property Existence Using the has Trap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
Preventing Property Deletion with the deleteProperty Trap . . . . . . . . . . . . . . . . . . . . . . 250
Prototype Proxy Traps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
How Prototype Proxy Traps Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
Why Two Sets of Methods? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Object Extensibility Traps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
Two Basic Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
Duplicate Extensibility Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
Property Descriptor Traps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
Blocking Object.defineProperty() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
Descriptor Object Restrictions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
Duplicate Descriptor Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
The ownKeys Trap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Function Proxies with the apply and construct Traps . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Validating Function Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
Calling Constructors Without new . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
Overriding Abstract Base Class Constructors . . . . . . . . . . . . . . . . . . . . . . . . 266
Callable Class Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
Revocable Proxies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
Solving the Array Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
Detecting Array Indexes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
Increasing length When Adding New Elements . . . . . . . . . . . . . . . . . . . . . . 270
Deleting Elements When Reducing length . . . . . . . . . . . . . . . . . . . . . . . . . . 272
Implementing the MyArray Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
Using a Proxy as a Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
Using the get Trap on a Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
Using the set Trap on a Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
Using the has Trap on a Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
Proxies as Prototypes on Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
xiv Contents in Detail
13
Encapsulating Code with Modules 283
What Are Modules? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
Basic Exporting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
Basic Importing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
Importing a Single Binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
Importing Multiple Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
Importing an Entire Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
A Subtle Quirk of Imported Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
Renaming Exports and Imports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
Default Values in Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Exporting Default Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Importing Default Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
Re-exporting a Binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Importing Without Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Loading Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
Using Modules in Web Browsers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
Browser Module Specifier Resolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
A
Minor Changes in ECMAScript 6 299
Working with Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
Identifying Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
Safe Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
New Math Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
Unicode Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
Formalizing the __proto__ Property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
B
Understanding ECMAScript 7 (2016) 305
The Exponentiation Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
Order of Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
Operand Restriction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
The Array.prototype.includes() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
How to Use Array.prototype.includes() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
Value Comparison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
A Change to Function-Scoped Strict Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
Index 311
Contents in Detail xv
Fore word
ECMAScript 6 has taken the world by storm. It came long after people
stopped waiting for it, and then it spread faster than most people could
learn it. Everybody has a different story about it. Here is mine.
In 2013, I worked at a startup that pivoted from iOS to the web. It was
before I co-created Redux or participated in the JavaScript open source
community. At the time, I was struggling to learn web development, and
I was terrified. My team had to build a web version of our product from
scratch in just a few months. In JavaScript.
At first I scoffed at the idea of writing something large in JavaScript.
But a new team member persuaded me that JavaScript was not a toy lan-
guage. I agreed to give it a try. I set my prejudices aside, opened MDN and
StackOverflow, and learned JavaScript in depth for the first time. The sim-
plicity I discovered enchanted me. My colleague also taught me how to use
tools such as a linter and a bundler. In a few weeks, I woke up and realized
that I enjoyed writing JavaScript.
But no language is perfect. I missed the frequent updates that I’d
come to expect after working with other languages. The only substan-
tial update to JavaScript in a decade, ECMAScript 5, was a mere cleanup
that nevertheless took years for browsers to fully support. At the time, the
upcoming ECMAScript 6 (ES6) specification, codenamed Harmony, was far
from finished and seemed like a distant future. “Maybe in 10 years I’ll get to
write some ES6 code,” I thought.
There were some experimental “transpilers” like Google Traceur that
translated code from ES6 into ES5. Most of them were very limited or hard
to plug into an existing JavaScript build pipeline. But then a new transpiler
called 6to5 came along and changed everything. It was easy to install, inte-
grated well with the existing tools, and produced readable code. It spread
like wildfire. Now called Babel, 6to5 brought ES6 features to a mainstream
audience even before the specification was finalized. In a matter of months,
ES6 was everywhere.
ES6 has divided the community for a number of reasons. As this book
goes to press, it is still not fully implemented in many major browsers.
Having a build step can be intimidating when you’re just learning the
language. Some libraries have documentation and examples in ES6, and
you might wonder if it is possible to use those libraries in ES5 at all. This
contributes to the confusion. Many people didn’t expect any new features
in the language because it had almost never changed before. Others anx-
iously awaited the new features’ arrival and used all of them together—in
some cases beyond what was necessary.
Just as I was becoming proficient with JavaScript, I felt that somebody
pulled the rug from under my feet, and now I had to learn a new language.
I felt bad about this for a few months. Finally, on Christmas Eve, I started
reading a draft of this book. I couldn’t put it down. Next thing I knew, it
was 3 am, everybody at the party was asleep, and I understood ES6!
Nicholas is an incredibly gifted teacher. He conveys deep details in a
straightforward way so they don’t go over your head. Apart from this book,
he is also known for creating ESLint, a JavaScript code analyzer that has
been downloaded millions of times.
Nicholas knows JavaScript like very few people do. Don’t miss the
chance to soak up some of his knowledge. Read this book, and you, too,
will become confident in your understanding of ES6.
Dan Abramov
React core team member and creator of Redux
xviii Foreword
acknowledgments
xxii Introduction
Browser and Node.js Compatibility
Many JavaScript environments, such as web browsers and Node.js, are
actively working on implementing ECMAScript 6. This book doesn’t
attempt to address the inconsistencies between implementations; instead,
it focuses on what the specification defines as the correct behavior. As
such, it’s possible that your JavaScript environment may not conform to
the behavior described in this book.
Overview
Each chapter and appendix in this book covers a different aspect of
ECMAScript 6. Many chapters start by discussing problems that ECMA
Script 6 changes were made to solve to give you a broader context for those
changes. All chapters include code examples to help you learn new syntax
and concepts.
• Chapter 1: Block Bindings talks about let and const, the block-level
replacement for var.
• Chapter 2: Strings and Regular Expressions covers additional func-
tionality for string manipulation and inspection as well as the introduc-
tion of template strings.
• Chapter 3: Functions discusses the various changes to functions,
including the arrow function form, default parameters, rest param-
eters, and a few other features.
• Chapter 4: Expanded Object Functionality explains the changes to
how objects are created, modified, and used. Topics include changes to
object literal syntax and new reflection methods.
• Chapter 5: Destructuring for Easier Data Access introduces object and
array destructuring, which allow you to decompose objects and arrays
using a concise syntax.
• Chapter 6: Symbols and Symbol Properties introduces the concept of
symbols, a new way to define properties. Symbols are a new primitive
type that you can use to obscure (but not hide) object properties and
methods.
Introduction xxiii
• Chapter 7: Sets and Maps details the new collection types of Set, WeakSet,
Map, and WeakMap. These types expand on the usefulness of arrays by add-
ing semantics, de-duping, and memory management designed specifi-
cally for JavaScript.
• Chapter 8: Iterators and Generators discusses the addition of iterators
and generators to the language. These features allow you to work with
collections of data in powerful ways that were not possible in previous
versions of JavaScript.
• Chapter 9: Introducing JavaScript Classes introduces the first formal
concept of classes in JavaScript. Often a point of confusion for those
coming from other languages, the addition of class syntax in JavaScript
makes the language more approachable to others and more concise for
enthusiasts.
• Chapter 10: Improved Array Capabilities details the changes to native
arrays and the useful new ways you can use them in JavaScript.
• Chapter 11: Promises and Asynchronous Programming introduces
promises as a new part of the language. Promises were a grassroots
effort that eventually took off and gained popularity due to extensive
library support. ECMAScript 6 formalizes promises and makes them
available by default.
• Chapter 12: Proxies and the Reflection API introduces the formalized
reflection API for JavaScript and the new proxy object that allows you to
intercept every operation performed on an object. Proxies give develop-
ers unprecedented control over objects and, as such, unlimited possi-
bilities for defining new interaction patterns.
• Chapter 13: Encapsulating Code with Modules details the official
module format for JavaScript. The intent is that these modules can
replace the numerous ad hoc module definition formats that have
appeared over the years.
• Appendix A: Minor Changes in ECMAScript 6 covers other changes
implemented in ECMAScript 6 that you’ll use less frequently or that
didn’t quite fit into the broader major topics covered in each chapter.
• Appendix B: Understanding ECMAScript 7 (2016) describes the three
additions to the standard that were implemented in ECMAScript 7,
which didn’t impact JavaScript nearly as much as ECMAScript 6.
Conventions Used
The following typographical conventions are used in this book:
xxiv Introduction
Additionally, longer code examples are contained in constant width
code blocks, such as the following:
function doSomething() {
// empty
}
console.log("Hi"); // "Hi"
Introduction xxv
1
Block Bindings
Hän esitti minulle asian. Runoilija Jouko Touko oli hänelle kertonut.
Arvasinhan minä.
Johtaja Muru oli jo teettänyt lautat valmiiksi. Niillä piti minun nyt
Helsingin herrojen kanssa hursalluttaa alas Kohisevan kuohuista.
»No… jos hän hukkuu, niin kyllä minä nain sitten teidät», ilvehdin
minä.
»Hyi teitä, kuinka olette jumalaton!»
»Se oli fiinisti tehty, herra Kuuskoski! En ole ennen sellaista nähnyt
Pakkasi ihan peloittamaan.»
Näin selvään, että tukkini kulki sitä kohti yhä kasvavalla vauhdilla…
Koetin lyödä sitä oikeaan mutta virta painoi sitä itsepintaisesti kiveä
kohti. Yhteentörmäys oli välttämätön.
»Te ette nyt ole nimismies, te olette runoilija ja tämä malja pitää
teidän nyt tyhjentää.»
Mutta — oli miten oli, minä olin jälleen saanut kokea, että maailma
vihaa soihdunkantajia. Niitä, jotka se myöhemmin on julistanut
merkkimiehikseen, se on kiduttanut ja vainonnut heidän eläessään.
Minun tarvitsee vai viitata historiaan. Kolumbus löysi Amerikan,
mutta toinen mies, turhanpäiväinen turisti, antoi sille nimensä.
Minun koskenlaskuni oli tavallaan Amerikan löytö sekin: se avasi
ainakin filmiyhtiö »Ihanteelle» tien rikkauteen. Mutta minä olen
jäänyt tästä rikkaudesta osattomaksi. Mainittu yhtiö on näytellyt
»Koskenlaskijan kosintaa» ympäri valtakunnan, myynyt filmin
Pohjanlahden toiselle puolenkin ja niittänyt sieltä kruunuja ja
kunniaa. Mutta minä, joka tein yrityksen mahdolliseksi, saan kitua
puutteessa uuden Kolumbuksen tavoin. Olen kuitenkin iloinen, että
olen tämän kautta tullut mahdolliseksi saamaan nimeni
sivistyshistorian lehdille. Sillä olen varma, että kun kerran kirjoitetaan
filmitaiteen historia, minun nimeni mainitaan siinä Chaplinin ja
Valentinen nimien rinnalla. Ja se lohduttaa minua.
Mutta silloin on jymy noussut kattoon, kun minä olen ottanut esille
okarinoni ja ruvennut soittamaan. Teofilus Tiennäyttäjä on nim.
hyvin musikaalinen. Häneen menee musiikki kuin häkä. Jos olen
soittanut surullista, on hän itkenyt, jos taas olen pyöräyttänyt ilon
puolelle, on hän nauranut ja tanssinut. Silloin ei häntä ole yhtään
haitannut hänen tavaton lihavuutensa. Olen toisinaan nähnyt hänen
tanssivan kyyneleet silmissä, kun »Voi äiti parasta» kiepahutin
»Eklöfin Aukustiksi». Minunkin on silloin täytynyt nauraa hänen
hölkälleen, kun hän on pomppinut tuolin kanssa juuri kuin kumipallo.
Hiki virtasi hänen pyöreiltä kasvoiltaan, jotka olivat vääntyneet
suloisen tuskalliseen irvistykseen. Hänen naamansa muistutti
sellaisina hetkinä kuuta, joka kyynel silmänurkassa nauraa.
Hän teki kädellään liikkeen kuin näyttääkseen, että tällä kertaa olin
minä tehnyt »piirron», ja lausahti:
Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.
textbookfull.com