- 快召唤伙伴们来围观吧
- 微博 QQ QQ空间 贴吧
- 文档嵌入链接
- 复制
- 微信扫一扫分享
- 已成功复制到剪贴板
javascript-handbook
展开查看详情
1 .
2 . Table of Contents Preface Introduction to JavaScript ECMAScript ES6 ES2016 ES2017 ES2018 Coding style Lexical Structure Variables Types Expressions Prototypal inheritance Classes Exceptions Semicolons Quotes Template Literals Functions Arrow Functions Closures Arrays Loops Events The Event Loop Asynchronous programming and callbacks Promises Async and Await Loops and Scope Timers 2
3 .this Strict Mode Immediately-invoked Function Expressions (IIFE) Math operators The Math object ES Modules CommonJS Glossary 3
4 .Preface Preface The JavaScript Handbook follows the 80/20 rule: learn in 20% of the time the 80% of a topic. I find this approach gives a well-rounded overview. This book does not try to cover everything under the sun related to JavaScript. If you think some specific topic should be included, tell me. You can reach me on Twitter @flaviocopes. I hope the contents of this book will help you achieve what you want: learn the basics of JavaScript. This book is written by Flavio. I publish web development tutorials every day on my website flaviocopes.com. Enjoy! 4
5 .Introduction to JavaScript Introduction to JavaScript JavaScript is one of the most popular programming languages in the world, and now widely used also outside of the browser. The rise of Node.js in the last few years unlocked backend development, once the domain of Java, Ruby, Python, PHP, and more traditional server-side languages. Learn all about it! Introduction JavaScript is one of the most popular programming languages in the world. Created 20 years ago, it's gone a very long way since its humble beginnings. Being the first - and the only - scripting language that was supported natively by web browsers, it simply stuck. In the beginnings, it was not nearly powerful as it is today, and it was mainly used for fancy animations and the marvel known at the time as DHTML. With the growing needs that the web platform demands, JavaScript had the responsibility to grow as well, to accommodate the needs of one of the most widely used ecosystems of the world. Many things were introduced in the platform, with browser APIs, but the language grew quite a lot as well. JavaScript is now widely used also outside of the browser. The rise of Node.js in the last few years unlocked backend development, once the domain of Java, Ruby, Python and PHP and more traditional server-side languages. JavaScript is now also the language powering databases and many more applications, and it's even possible to develop embedded applications, mobile apps, TV sets apps and much more. What started as a tiny language inside the browser is now the most popular language in the world. A basic definition of JavaScript JavaScript is a programming language that is: high level: it provides abstractions that allow you to ignore the details of the machine where it's running on. It manages memory automatically with a garbage collector, so you 5
6 .Introduction to JavaScript can focus on the code instead of managing memory locations, and provides many constructs which allow you to deal with highly powerful variables and objects. dynamic: opposed to static programming languages, a dynamic language executes at runtime many of the things that a static language does at compile time. This has pros and cons, and it gives us powerful features like dynamic typing, late binding, reflection, functional programming, object runtime alteration, closures and much more. dynamically typed: a variable does not enforce a type. You can reassign any type to a variable, for example assigning an integer to a variable that holds a string. weakly typed: as opposed to strong typing, weakly (or loosely) typed languages do not enforce the type of an object, allowing more flexibility but denying us type safety and type checking (something that TypeScript and Flow aim to improve) interpreted: it's commonly known as an interpreted language, which means that it does not need a compilation stage before a program can run, as opposed to C, Java or Go for example. In practice, browsers do compile JavaScript before executing it, for performance reasons, but this is transparent to you: there is no additional step involved. multi-paradigm: the language does not enforce any particular programming paradigm, unlike Java for example which forces the use of object oriented programming, or C that forces imperative programming. You can write JavaScript using an object-oriented paradigm, using prototypes and the new (as of ES6) classes syntax. You can write JavaScript in functional programming style, with its first class functions, or even in an imperative style (C-like). In case you're wondering, JavaScript has nothing to do with Java, it's a poor name choice but we have to live with it. JavaScript versions Let me introduce the term ECMAScript here. We have a complete guide dedicated to ECMAScript where you can dive into it more, but to start with, you just need to know that ECMAScript (also called ES) is the name of the JavaScript standard. JavaScript is an implementation of that standard. That's why you'll hear about ES6, ES2015, ES2016, ES2017, ES2018 and so on. For a very long time, the version of JavaScript that all browser ran was ECMAScript 3. Version 4 was canceled due to feature creep (they were trying to add too many things at once), while ES5 was a huge version for JS. ES2015, also called ES6, was huge as well. Since then, the ones in charge decided to release one version per year, to avoid having too much time idle between releases, and have a faster feedback loop. 6
7 .Introduction to JavaScript Currently, the latest approved JavaScript version is ES2017. 7
8 .ECMAScript ECMAScript ECMAScript is the standard upon which JavaScript is based, and it's often abbreviated to ES. Discover everything about ECMAScript, and the last features added in ES6, 7, 8 Whenever you read about JavaScript you'll inevitably see one of these terms: ES3 ES5 ES6 ES7 ES8 ES2015 ES2016 ES2017 ECMAScript 2017 ECMAScript 2016 8
9 .ECMAScript ECMAScript 2015 What do they mean? They are all referring to a standard, called ECMAScript. ECMAScript is the standard upon which JavaScript is based, and it's often abbreviated to ES. Beside JavaScript, other languages implement(ed) ECMAScript, including: ActionScript (the Flash scripting language), which is losing popularity since Flash will be officially discontinued in 2020 JScript (the Microsoft scripting dialect), since at the time JavaScript was supported only by Netscape and the browser wars were at their peak, Microsoft had to build its own version for Internet Explorer but of course JavaScript is the most popular and widely used implementation of ES. Why this weird name? Ecma International is a Swiss standards association who is in charge of defining international standards. When JavaScript was created, it was presented by Netscape and Sun Microsystems to Ecma and they gave it the name ECMA-262 alias ECMAScript. This press release by Netscape and Sun Microsystems (the maker of Java) might help figure out the name choice, which might include legal and branding issues by Microsoft which was in the committee, according to Wikipedia. After IE9, Microsoft stopped stopped branding its ES support in browsers as JScript and started calling it JavaScript (at least, I could not find references to it any more) So as of 201x, the only popular language supporting the ECMAScript spec is JavaScript. Current ECMAScript version The current ECMAScript version is ES2018. It was released in June 2018. When is the next version coming out? Historically JavaScript editions have been standardized during the summer, so we can expect ECMAScript 2019 to be released in summer 2019, but this is just speculation. 9
10 .ECMAScript What is TC39 TC39 is the committee that evolves JavaScript. The members of TC39 are companies involved in JavaScript and browser vendors, including Mozilla, Google, Facebook, Apple, Microsoft, Intel, PayPal, SalesForce and others. Every standard version proposal must go through various stages, which are explained here. ES Versions I found it puzzling why sometimes an ES version is referenced by edition number and sometimes by year, and I am confused by the year by chance being -1 on the number, which adds to the general confusion around JS/ES Before ES2015, ECMAScript specifications were commonly called by their edition. So ES5 is the official name for the ECMAScript specification update published in 2009. Why does this happen? During the process that led to ES2015, the name was changed from ES6 to ES2015, but since this was done late, people still referenced it as ES6, and the community has not left the edition naming behind - the world is still calling ES releases by edition number. This table should clear things a bit: Edition Official name Date published ES9 ES2018 June 2018 ES8 ES2017 June 2017 ES7 ES2016 June 2016 ES6 ES2015 June 2015 ES5.1 ES5.1 June 2011 ES5 ES5 December 2009 ES4 ES4 Abandoned ES3 ES3 December 1999 ES2 ES2 June 1998 ES1 ES1 June 1997 ES Next 10
11 .ECMAScript ES.Next is a name that always indicates the next version of JavaScript. So at the time of writing, ES9 has been released, and ES.Next is ES10 11
12 .ES6 ES6 ECMAScript is the standard upon which JavaScript is based, and it's often abbreviated to ES. Discover everything about ECMAScript, and the last features added in ES6, aka ES2015 ECMAScript 2015, also known as ES6, is a fundamental version of the ECMAScript standard. Published 4 years after the latest standard revision, ECMAScript 5.1, it also marked the switch from edition number to year number. So it should not be named as ES6 (although everyone calls it as such) but ES2015 instead. ES5 was 10 years in the making, from 1999 to 2009, and as such it was also a fundamental and very important revision of the language, but now much time has passed that it's not worth discussing how pre-ES5 code worked. Since this long time passed between ES5.1 and ES6, the release is full of important new features and major changes in suggested best practices in developing JavaScript programs. To understand how fundamental ES2015 is, just keep in mind that with this version, the specification document went from 250 pages to ~600. The most important changes in ES2015 include Arrow functions Promises Generators let and const Classes Modules Multiline strings Template literals Default parameters The spread operator| Destructuring assignments Enhanced object literals The for..of loop Map and Set Each of them has a dedicated section in this article. Arrow Functions 12
13 .ES6 Arrow functions since their introduction changed how most JavaScript code looks (and works). Visually, it's a simple and welcome change, from: const foo = function foo() { //... } to const foo = () => { //... } And if the function body is a one-liner, just: const foo = () => doSomething() Also, if you have a single parameter, you could write: const foo = param => doSomething(param) This is not a breaking change, regular function s will continue to work just as before. A new this scope The this scope with arrow functions is inherited from the context. With regular function s this always refers to the nearest function, while with arrow functions this problem is removed, and you won't need to write var that = this ever again. Promises Promises (check the full guide to promises) allow us to eliminate the famous "callback hell", although they introduce a bit more complexity (which has been solved in ES2017 with async , a higher level construct). Promises have been used by JavaScript developers well before ES2015, with many different libraries implementations (e.g. jQuery, q, deferred.js, vow...), and the standard put a common ground across differences. By using promises you can rewrite this code 13
14 .ES6 setTimeout(function() { console.log('I promised to run after 1s') setTimeout(function() { console.log('I promised to run after 2s') }, 1000) }, 1000) as const wait = () => new Promise((resolve, reject) => { setTimeout(resolve, 1000) }) wait().then(() => { console.log('I promised to run after 1s') return wait() }) .then(() => console.log('I promised to run after 2s')) Generators Generators are a special kind of function with the ability to pause itself, and resume later, allowing other code to run in the meantime. The code decides that it has to wait, so it lets other code "in the queue" to run, and keeps the right to resume its operations "when the thing it's waiting for" is done. All this is done with a single, simple keyword: yield . When a generator contains that keyword, the execution is halted. A generator can contain many yield keywords, thus halting itself multiple times, and it's identified by the *function keyword, which is not to be confused with the pointer dereference operator used in lower level programming languages such as C, C++ or Go. Generators enable whole new paradigms of programming in JavaScript, allowing: 2-way communication while a generator is running long-lived while loops which do not freeze your program Here is an example of a generator which explains how it all works. function *calculator(input) { var doubleThat = 2 * (yield (input / 2)) var another = yield (doubleThat) return (input * doubleThat * another) } 14
15 .ES6 We initialize it with const calc = calculator(10) Then we start the iterator on our generator: calc.next() This first iteration starts the iterator. The code returns this object: { done: false value: 5 } What happens is: the code runs the function, with input = 10 as it was passed in the generator constructor. It runs until it reaches the yield , and returns the content of yield : input / 2 = 5 . So we got a value of 5, and the indication that the iteration is not done (the function is just paused). In the second iteration we pass the value 7 : calc.next(7) and what we got back is: { done: false value: 14 } 7 was placed as the value of doubleThat . Important: you might read like input / 2 was the argument, but that's just the return value of the first iteration. We now skip that, and use the new input value, 7 , and multiply it by 2. We then reach the second yield, and that returns doubleThat , so the returned value is 14 . In the next, and last, iteration, we pass in 100 calc.next(100) and in return we got 15
16 .ES6 { done: true value: 14000 } As the iteration is done (no more yield keywords found) and we just return (input * doubleThat * another) which amounts to 10 * 14 * 100 . let and const var is traditionally function scoped. let is a new variable declaration which is block scoped. This means that declaring let variables in a for loop, inside an if or in a plain block is not going to let that variable "escape" the block, while var s are hoisted up to the function definition. const is just like let , but immutable. In JavaScript moving forward, you'll see little to no var declarations any more, just let and const . const in particular, maybe surprisingly, is very widely used nowadays with immutability being very popular. Classes Traditionally JavaScript is the only mainstream language with prototype-based inheritance. Programmers switching to JS from class-based language found it puzzling, but ES2015 introduced classes, which are just syntactic sugar over the inner working, but changed a lot how we build JavaScript programs. Now inheritance is very easy and resembles other object-oriented programming languages: class Person { constructor(name) { this.name = name } hello() { return 'Hello, I am ' + this.name + '.' } } 16
17 .ES6 class Actor extends Person { hello() { return super.hello() + ' I am an actor.' } } var tomCruise = new Actor('Tom Cruise') tomCruise.hello() (the above program prints "Hello, I am Tom Cruise. I am an actor.") Classes do not have explicit class variable declarations, but you must initialize any variable in the constructor. Constructor Classes have a special method called constructor which is called when a class is initialized via new . Super The parent class can be referenced using super() . Getters and setters A getter for a property can be declared as class Person { get fullName() { return `${this.firstName} ${this.lastName}` } } Setters are written in the same way: class Person { set age(years) { this.theAge = years } } Modules 17
18 .ES6 Before ES2015, there were at least 3 major modules competing standards, which fragmented the community: AMD RequireJS CommonJS ES2015 standardized these into a common format. Importing modules Importing is done via the import ... from ... construct: import * from 'mymodule' import React from 'react' import { React, Component } from 'react' import React as MyLibrary from 'react' Exporting modules You can write modules and export anything to other modules using the export keyword: export var foo = 2 export function bar() { /* ... */ } Template Literals Template literals are a new syntax to create strings: const aString = `A string` They provide a way to embed expressions into strings, effectively interpolating the values, by using the ${a_variable} syntax: const var = 'test' const string = `something ${var}` //something test You can perform more complex expressions as well: const string = `something ${1 + 2 + 3}` const string2 = `something ${foo() ? 'x' : 'y' }` 18
19 .ES6 and strings can span over multiple lines: const string3 = `Hey this string is awesome!` Compare how we used to do multiline strings pre-ES2015: var str = 'One\n' + 'Two\n' + 'Three' See this post for an in-depth guide on template literals Default parameters Functions now support default parameters: const foo = function(index = 0, testing = true) { /* ... */ } foo() The spread operator You can expand an array, an object or a string using the spread operator ... . Let's start with an array example. Given const a = [1, 2, 3] you can create a new array using const b = [...a, 4, 5, 6] You can also create a copy of an array using const c = [...a] This works for objects as well. Clone an object with: 19
20 .ES6 const newObj = { ...oldObj } Using strings, the spread operator creates an array with each char in the string: const hey = 'hey' const arrayized = [...hey] // ['h', 'e', 'y'] This operator has some pretty useful applications. The most important one is the ability to use an array as function argument in a very simple way: const f = (foo, bar) => {} const a = [1, 2] f(...a) (in the past you could do this using f.apply(null, a) but that's not as nice and readable) Destructuring assignments Given an object, you can extract just some values and put them into named variables: const person = { firstName: 'Tom', lastName: 'Cruise', actor: true, age: 54, //made up } const {firstName: name, age} = person name and age contain the desired values. The syntax also works on arrays: const a = [1,2,3,4,5] [first, second, , , fifth] = a Enhanced Object Literals In ES2015 Object Literals gained superpowers. Simpler syntax to include variables 20
21 .ES6 Instead of doing const something = 'y' const x = { something: something } you can do const something = 'y' const x = { something } Prototype A prototype can be specified with const anObject = { y: 'y' } const x = { __proto__: anObject } super() const anObject = { y: 'y', test: () => 'zoo' } const x = { __proto__: anObject, test() { return super.test() + 'x' } } x.test() //zoox Dynamic properties const x = { ['a' + '_' + 'b']: 'z' } x.a_b //z For-of loop 21
22 .ES6 ES5 back in 2009 introduced forEach() loops. While nice, they offered no way to break, like for loops always did. ES2015 introduced the for-of loop, which combines the conciseness of forEach with the ability to break: //iterate over the value for (const v of ['a', 'b', 'c']) { console.log(v); } //get the index as well, using `entries()` for (const [i, v] of ['a', 'b', 'c'].entries()) { console.log(i, v); } Map and Set Map and Set (and their respective garbage collected WeakMap and WeakSet) are the official implementations of two very popular data structures. 22
23 .ES2016 ES2016 ECMAScript is the standard upon which JavaScript is based, and it's often abbreviated to ES. Discover everything about ECMAScript, and the last features added in ES2016, aka ES7 ES7, officially known as ECMAScript 2016, was finalized in June 2016. Compared to ES6, ES7 is a tiny release for JavaScript, containing just two features: Array.prototype.includes Exponentiation Operator Array.prototype.includes() This feature introduces a more readable syntax for checking if an array contains an element. With ES6 and lower, to check if an array contained an element you had to use indexOf , which checks the index in the array, and returns -1 if the element is not there. Since -1 is evaluated as a true value, you could not do for example if (![1,2].indexOf(3)) { console.log('Not found') } With this feature introduced in ES7 we can do if (![1,2].includes(3)) { console.log('Not found') } Exponentiation Operator The exponentiation operator ** is the equivalent of Math.pow() , but brought into the language instead of being a library function. Math.pow(4, 2) == 4 ** 2 This feature is a nice addition for math intensive JS applications. 23
24 .ES2016 The ** operator is standardized across many languages including Python, Ruby, MATLAB, Lua, Perl and many others. 24
25 .ES2017 ES2017 ECMAScript is the standard upon which JavaScript is based, and it's often abbreviated to ES. Discover everything about ECMAScript, and the last features added in ES2017, aka ES8 ECMAScript 2017, edition 8 of the ECMA-262 Standard (also commonly called ES2017 or ES8), was finalized in June 2017. Compared to ES6, ES8 is a tiny release for JavaScript, but still it introduces very useful features: String padding Object.values Object.entries Object.getOwnPropertyDescriptors() Trailing commas in function parameter lists and calls Async functions Shared memory and atomics String padding The purpose of string padding is to add characters to a string, so it reaches a specific length. ES2017 introduces two String methods: padStart() and padEnd() . padStart(targetLength [, padString]) padEnd(targetLength [, padString]) Sample usage: padStart() 'test'.padStart(4) 'test' 'test'.padStart(5) '_test' 'test'.padStart(8) '____test' 'test'.padStart(8, 'abcd') 'abcdtest' padEnd() 'test'.padEnd(4) 'test' 25
26 .ES2017 'test'.padEnd(5) 'test_' 'test'.padEnd(8) 'test____' 'test'.padEnd(8, 'abcd') 'testabcd' (in the table, _ = space) Object.values() This method returns an array containing all the object own property values. Usage: const person = { name: 'Fred', age: 87 } Object.values(person) // ['Fred', 87] Object.values() also works with arrays: const people = ['Fred', 'Tony'] Object.values(people) // ['Fred', 'Tony'] Object.entries() This method returns an array containing all the object own properties, as an array of [key, value] pairs. Usage: const person = { name: 'Fred', age: 87 } Object.entries(person) // [['name', 'Fred'], ['age', 87]] Object.entries() also works with arrays: const people = ['Fred', 'Tony'] Object.entries(people) // [['0', 'Fred'], ['1', 'Tony']] getOwnPropertyDescriptors() This method returns all own (non-inherited) properties descriptors of an object. Any object in JavaScript has a set of properties, and each of these properties has a descriptor. 26
27 .ES2017 A descriptor is a set of attributes of a property, and it's composed by a subset of the following: value: the value of the property writable: true the property can be changed get: a getter function for the property, called when the property is read set: a setter function for the property, called when the property is set to a value configurable: if false, the property cannot be removed nor any attribute can be changed, except its value enumerable: true if the property is enumerable Object.getOwnPropertyDescriptors(obj) accepts an object, and returns an object with the set of descriptors. In what way is this useful? ES2015 gave us Object.assign() , which copies all enumerable own properties from one or more objects, and return a new object. However there is a problem with that, because it does not correctly copies properties with non- default attributes. If an object for example has just a setter, it's not correctly copied to a new object, using Object.assign() . For example with const person1 = { set name(newName) { console.log(newName) } } This won't work: const person2 = {} Object.assign(person2, person1) But this will work: const person3 = {} Object.defineProperties(person3, Object.getOwnPropertyDescriptors(person1)) As you can see with a simple console test: person1.name = 'x' 27
28 .ES2017 ;('x') person2.name = 'x' person3.name = 'x' ;('x') person2 misses the setter, it was not copied over. The same limitation goes for shallow cloning objects with Object.create(). Trailing commas This feature allows to have trailing commas in function declarations, and in functions calls: const doSomething = (var1, var2) => { //... } doSomething('test2', 'test2') This change will encourage developers to stop the ugly "comma at the start of the line" habit. Async functions Check the dedicated post about async/await ES2017 introduced the concept of async functions, and it's the most important change introduced in this ECMAScript edition. Async functions are a combination of promises and generators to reduce the boilerplate around promises, and the "don't break the chain" limitation of chaining promises. Why they are useful It's a higher level abstraction over promises. When Promises were introduced in ES2015, they were meant to solve a problem with asynchronous code, and they did, but over the 2 years that separated ES2015 and ES2017, it was clear that promises could not be the final solution. Promises were introduced to solve the famous callback hell problem, but they introduced complexity on their own, and syntax complexity. They were good primitives around which a better syntax could be exposed to the developers: enter async functions. 28
29 .ES2017 A quick example Code making use of asynchronous functions can be written as function doSomethingAsync() { return new Promise(resolve => { setTimeout(() => resolve('I did something'), 3000) }) } async function doSomething() { console.log(await doSomethingAsync()) } console.log('Before') doSomething() console.log('After') The above code will print the following to the browser console: Before After I did something //after 3s Multiple async functions in series Async functions can be chained very easily, and the syntax is much more readable than with plain promises: function promiseToDoSomething() { return new Promise(resolve => { setTimeout(() => resolve('I did something'), 10000) }) } async function watchOverSomeoneDoingSomething() { const something = await promiseToDoSomething() return something + ' and I watched' } async function watchOverSomeoneWatchingSomeoneDoingSomething() { const something = await watchOverSomeoneDoingSomething() return something + ' and I watched as well' } watchOverSomeoneWatchingSomeoneDoingSomething().then(res => { console.log(res) }) 29