This article presents the functionality provided by all ECMAScript versions in reverse order, with code examples or a simple listing. Designed to provide you with a cheat sheet of ECMAScript features as you code
ES2021-ES12
String.protype.replaceAll
Before ES2021, to replace all specified characters in a string, we could do:
const str = "a+b+c+"; const newStr = str.replace(/\+/g, "🤣"); console.log(newStr); //a🤣b🤣c🤣
ES2021 proposes the replaceAll method and mounts it on the prototype of String, which can be used like this:
const str = "a+b+c+"; const newStr = str.replaceAll("+", "🤣"); console.log(newStr); //a🤣b🤣c🤣
Promise.any
Promise.any
- Receives an iterable of Promises, and as long as any one of the promise s succeeds, return the one that has succeeded
- If all promises fail/reject, return a failed promise
Comparison of Promise.race:
- Whenever the state of any promise changes (whether it succeeds or fails), return that promise
Comparison of Promise.all
- As long as any promise fails, return the failed promise
- When all asynchronous operations are successful, the promise is returned, and the return value forms an array
const pErr = new Promise((resolve, reject) => { reject("always fail"); }); const pSlow = new Promise((resolve, reject) => { setTimeout(resolve, 500, "finalized"); }); const pFast = new Promise((resolve, reject) => { setTimeout(resolve, 100, "Finish quickly"); }); // Use .then .catch Promise.any([pErr, pSlow, pFast]) .then((value) => { // Returns the first successful promise, ie: pFast-"completes soon" console.log(value); }) .catch((err) => { // Fired when all promise s fail }); // Use async-await try { const first = await Promise.any(promises); // Either promise returns successfully. console.log(first); } catch (error) { // all promise s failed console.log(error); }
WeakRef
The WeakRef proposal mainly contains two new features:
- You can create a weak reference to an object through the WeakRef class
- You can use the FinalizationRegistry class to execute some custom methods after an object is garbage collected
The above two new features can be used together or separately, depending on your needs. A WeakRef object contains a weak reference to an object, called a target or reference. By weakly referencing an object, the object can be reclaimed by the garbage collection mechanism without other references. WeakRef is mainly used to cache and map some large objects. You can use it when you want an object to be garbage collected in time without being referenced elsewhere.
function toogle(element) { const weakElement = new WeakRef(element); let intervalId = null; function toggle() { const el = weakElement.deref(); if (!el) { return clearInterval(intervalId); } const decoration = weakElement.style.textDecoration; const style = decoration === "none" ? "underline" : "none"; decoration = style; } intervalId = setInterval(toggle, 1000); } const element = document.getElementById("link"); toogle(element); setTimeout(() => element.remove(), 10000);
FinalizationRegistry receives a registrar callback function, which can be used to register an event listener for the specified object. When the object is garbage collected, the monitored event will be triggered. The specific steps are as follows. First, create a registrar:
const registry = new FinalizationRegistry((heldValue) => { // .... });
Then register a specified object, and you can also pass some parameters to the registrar callback:
registry.register(theObject, "some value");
logical assignment operator
Reference for details ts39-proposal-logical-assignment
Logical assignment operators combine logical operators and assignment expressions. There are two types of logical assignment operators:
- or equal (||=)
- And equal to (& & =)
- ??=
||=
const giveKey = () => { return "somekey"; }; let userDetails = { name: "chika", age: 5, room: 10, key: "" }; userDetails.key ||= giveKey(); console.log(userDetails.key); //output : somekey
&&=
const deleteKey = () => { return " "; }; let userDetails = { name: "chika", age: 5, room: 10, key: "990000" }; userDetails.key &&= deleteKey(); console.log(userDetails.key); //output : ""
??= Null assignment operator
??= is also known as the null assignment operator and is related to the non-null operator above. Check out the connection between them:
var x = null; var y = 5; console.log((x ??= y)); // => 5 console.log((x = x ?? y)); // => 5
This assignment operator only assigns a value if the value is null or undefined. The above example highlights that this operator is essentially syntactic sugar for null assignment (similar syntactic sugar: a = a + b can be written as a += b ). Next, let's see how this operator differs from default parameters (default parameters are a new syntax introduced by ES6 that only sets a default value to a function parameter when it is undefined ):
function gameSettingsWithNullish(options) { options.gameSpeed ??= 1; options.gameDiff ??= "easy"; return options; } function gameSettingsWithDefaultParams(gameSpeed = 1, gameDiff = "easy") { return { gameSpeed, gameDiff }; } gameSettingsWithNullish({ gameSpeed: null, gameDiff: null }); // => {gameSpeed: 1, gameDiff: 'easy'} gameSettingsWithDefaultParams(undefined, null); // => {gameSpeed: null, gameDiff: null}
There is one notable difference in the way the above functions handle null values. The default parameter will override the default value with an empty parameter (an empty parameter here, which can only be undefined), and the null assignment operator will not. Neither default parameters nor null assignments will overwrite undefined values. Official MDN Documentation
const getKey = () => { return "somekey"; }; let userDetails = { name: "chika", age: 5, room: 10 }; userDetails.key ??= getKey(); console.log(userDetails.key); //output : "somekey"
number separator
With this function, we use \_, U+005F separator to group numbers to improve the readability of numbers:
1_000_000_000; // billion 101_475_938.38; // hundreds of millions const amount = 12345_00; // 12,345 const amount = 123_4500; // 123.45 (4 decimal places) const amount = 1_234_500; // 1,234,500 0.000_001; // millionth 1e10_000; // 10^10000 // const binary_literals = 0b1010_0001_1000_0101; const hex_literals = 0xa0_b0_c0; // const bigInt_literals = 1_000_000_000_000n; // const octal_literal = 0o1234_5670;
ES2020-ES11
ES2020 is the ECMAScript version corresponding to 2020
String.protype.matchAll
The matchAll() method returns all matches of a regular expression in the current string
However, it returns an Iterator, not an array. Converting a iterator to an array is very simple, just use the ... operator and the Array.from() method.
const string = "test1test2test3"; const regex = /t(e)(st(\d?))/g; const newdata = string.matchAll(regex); for (const match of newdata) { console.log(match); } // ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"] // ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"] // ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"] // Method 1 to convert to an array [...newdata]; // Method 2 to convert to an array Array.from(newdata);
Detailed content reference ES Getting Started - matchAll
Dynamic import
The import(specifier) function supports dynamic loading of modules. The parameter specifier of the import function specifies the location of the module to be loaded. What parameters can the import command accept, and what parameters can the import() function accept, the difference between the two is that the latter is dynamically loaded.
import() returns a Promise object
const someVariable = "user"; import(`./some-modules/${someVariable}.js`) .then((module) => { // Business logic module.loadPageInto(main); }) .catch((err) => { // Failed to load });
Detailed content reference ES Getting Started - import
Promise.allSettled
The Promise.allSettled() method accepts a set of Promise instances as a parameter, wrapped into a new Promise instance. The wrapper instance does not end until all these parameter instances return results, whether fulfilled or rejected
Sometimes, we don't care about the results of asynchronous requests, we only care about whether all requests have ended. At this time, the Promise.allSettled() method is very useful
const promises = [fetch("index.html"), fetch("https://does-not-exist/")]; const results = await Promise.allSettled(promises); // Filter out successful requests const successfulPromises = results.filter((p) => p.status === "fulfilled"); // Filter out failed requests and output the reason const errors = results .filter((p) => p.status === "rejected") .map((p) => p.reason);
globalThis
Before ES2020, obtaining this in different environments needs to be encapsulated as follows
const getGlobalThis = () => { // in a webworker or service worker if (typeof self !== "undefined") return self; // in the browser if (typeof window !== "undefined") return window; // in Node.js if (typeof global !== "undefined") return global; // Standalone JavaScript shell if (typeof this !== "undefined") return this; throw new Error("Unable to locate global object"); }; const theGlobalThis = getGlobalThis(); if (typeof theGlobalThis.setTimeout !== "function") { // There is no setTimeout method in this environment! }
Now, globalThis provides a standard way to get the global this object (that is, the global object itself) in different contexts
if (typeof globalThis.setTimeout !== "function") { // There is no setTimeout method in this environment! }
Reference for details MDN-globalThis
Nullish coalescing Operator
In JS, the ?? operator is called the non-null operator. If the first parameter is not null/undefined (there are only two false values here, but the JS middle false values include: undefined, empty object null, value 0, empty number NaN, Boolean false, empty string '', do not mix), will return the first parameter, otherwise return the second parameter for example,
null ?? 5; // => 5 3 ?? 5; // => 3
When setting a default value for a variable, the || logical OR operator used to be used, for example,
const prevMoney = 1; const currMoney = 0; const noAccount = null; const futureMoney = -1; function moneyAmount(money) { return money || `Account not opened`; } console.log(moneyAmount(prevMoney)); // => 1 console.log(moneyAmount(currMoney)); // => Account not opened console.log(moneyAmount(noAccount)); // =>Account not opened console.log(moneyAmount(futureMoney)); // => -1
Above we created the function moneyAmount which returns the current user balance. We use the || operator to identify users without an account. However, what does it mean when the user doesn't have an account? It is more accurate to treat no account as empty rather than 0, since bank accounts may have no (or negative) currency. In the above example, the || operator treats 0 as a bogus value and should not include accounts where users have $0. Let's use the ?? non-null operator to solve this problem:
const currMoney = 0; const noAccount = null; function moneyAmount(money) { return money ?? `Account not opened`; } moneyAmount(currMoney); // => 0 moneyAmount(noAccount); // =>` account not opened`
In a nutshell, the ?? operator allows us to specify default values while ignoring false values such as 0 and empty strings.
Optional Chaining
?. Also known as the chain judgment operator. It allows developers to read property values deeply nested in object chains without having to verify every reference. When the reference is empty, the expression stops evaluating and returns undefined. for example:
var travelPlans = { destination: "DC", monday: { location: "National Mall", budget: 200, }, }; console.log(travelPlans.tuesday?.location); // => undefined
Now, combine what we just learned
function addPlansWhenUndefined(plans, location, budget) { if (plans.tuesday?.location == undefined) { var newPlans = { plans, tuesday: { location: location ?? "garden", budget: budget ?? 200, }, }; } else { newPlans ??= plans; // Override only if newPlans is undefined console.log("Scheduled"); } return newPlans; } // The initial value of the object travelPlans, from the example above var newPlans = addPlansWhenUndefined(travelPlans, "Ford theater", null); console.log(newPlans); // => { plans: // { destination: 'DC', // monday: { location: 'National Mall', budget: 200 } }, // tuesday: { location: 'Ford Theater', budget: 200 } } newPlans = addPlansWhenUndefined(newPlans, null, null); // Logs = > scheduled // returns => newPlans object
The above example contains all the operators we have learned so far. Now we've created a function that adds the schedule to the object tuesday.location which currently has no nested properties. We also used the non-null operator to provide a default value. This function will incorrectly accept a value like "0" as a valid parameter. This means that budget can be set to zero without any errors.
BigInt primitive type
The largest integer of the old version of JS standard can only be 253 - 1. Now BigInt is used to represent integers. There is no limit on the number of digits, and integers of any number of digits can be accurately represented. This is yet another data type from ECMAScript.
A BigInt can be defined by appending n to an integer literal, such as 10n, or by calling the function BigInt().
const theBiggestInt = 9007199254740991n; const alsoHuge = BigInt(9007199254740991); // ↪ 9007199254740991n
ES2019-ES10
Array#{flat,flatMap}
The members of an array are sometimes arrays, and Array.prototype.flat() is used to "flatten" a nested array into a one-dimensional array. This method returns a new array and has no effect on the original data.
[1, 2, [3, 4]].flat(); // [1, 2, 3, 4]
flatMap() can only expand one level of array.
// Equivalent to [[[2]], [[4]], [[6]], [[8]]].flat() [1, 2, 3, 4].flatMap((x) => [[x * 2]]); // [[2], [4], [6], [8]]
Reference for details ES Getting Started - flat
Object.fromEntries
The Object.fromEntries() method is the inverse of Object.entries() and is used to convert an array of key-value pairs into objects.
Object.fromEntries([ ["foo", "bar"], ["baz", 42], ]); // { foo: "bar", baz: 42 }
The main purpose of this method is to restore the data structure of key-value pairs into objects, so it is especially suitable for converting Map structures into objects.
// Example 1 const entries = new Map([ ["foo", "bar"], ["baz", 42], ]); Object.fromEntries(entries); // { foo: "bar", baz: 42 } // Example 2 const map = new Map().set("foo", true).set("bar", false); Object.fromEntries(map); // { foo: true, bar: false }
String#{trimStart,trimEnd}
ES2019 adds two methods, trimStart() and trimEnd(), to string instances. They behave the same as trim(), trimStart() removes whitespace at the beginning of the string, and trimEnd() removes whitespace at the end. They all return new strings and do not modify the original strings.
const s = " abc "; s.trim(); // "abc" s.trimStart(); // "abc " s.trimEnd(); // " abc"
Symbol#description
ES2019 provides an instance property description that directly returns the description of the Symbol.
// When creating a Symbol, you can add a description. const sym = Symbol("foo"); sym.description; // "foo"
In the above code, the description of sym is the string foo.
try { } catch {} // optional binding
The catch clause in older versions of try/catch statements required a variable. Now you can leave it out
// old version try { console.log(a); } catch (error) { console.log("reported an error"); } // ES2019-SE10 try { console.log(a); } catch { console.log("reported an error"); }
U+2028 and U+2029
In versions prior to ES2019, unescaped is not accepted
- line separator U+2028
- Paragraph separator U+2029
ES2019 allows direct input of U+2028 (line separator) and U+2029 (segment separator) for JavaScript strings.
/* ES2019 Previously, the following code would throw an error ES2019 The code below will not throw an error. */ const PS = eval("'\u2029'");
Introduction to ES - U+2028 and U+2029
Retrofit of JSON-stringify-
To ensure that legal UTF-8 characters are returned, ES2019 changed the behavior of JSON.stringify() . If it encounters a single code point between 0xD800 and 0xDFFF, or a non-existent paired form, it returns an escaped string and leaves it to the application to decide what to do next.
JSON.stringify("\u{D834}"); // ""\\uD834"" JSON.stringify("\uDF06\uD834"); // ""\\udf06\\ud834""
ES Getting Started - JSON-stringify- Retrofit
Stable sort for Array.prototype.sort()
Earlier ECMAScript did not specify whether the default sorting algorithm of Array.prototype.sort() is stable or not, leaving it to the browser to decide, which makes some implementations unstable. ES2019 explicitly states that the default sorting algorithm for Array.prototype.sort() must be stable. This provision has been fulfilled, and the default sorting algorithms for all major JavaScript implementations are now stable.
const arr = ["peach", "straw", "apple", "spork"]; const stableSorting = (s1, s2) => { if (s1[0] < s2[0]) return -1; return 1; }; arr.sort(stableSorting); // ["apple", "peach", "straw", "spork"]
Getting Started with ES - Sorting Stability
revised Function#toString
ES2019 made changes to the toString() method of function instances.
The toString() method returns the function code itself, previously omitting comments and spaces.
function /* foo comment */ foo() {} // old version foo.toString(); // function foo() {} // new version foo.toString(); // "function /* foo comment */ foo () {}"
ES2018-ES9
Lifting template literal restriction.
ES2018 has loosened restrictions on string escaping in tag templates. If an illegal string escape is encountered, undefined is returned instead of an error, and the original string can be obtained from the raw property.
function tag(strs) { strs[0] === undefined strs.raw[0] === "\\unicode and \\u{55}"; } tag`\unicode and \u{55}`
In the above code, the template string should have reported an error originally, but since the restriction on string escaping has been relaxed, no error will be reported. The JavaScript engine sets the first character to undefined, but the raw property can still get the original string , so the tag function can still process the original string.
- Getting Started with ES - Limitations of Template Strings
- ES Getting Started - row
- ES Getting Started - Modifier: u
Regular s modifier: dotAll mode-(s (dotAll) flag for regular expressions).
ES2018 introduced the s modifier so that . can match any single character.
/foo.bar/s.test("foo\nbar"); // true
This is called dotAll mode, that is, dots represent all characters. Therefore, the regular expression also introduces a dotAll property, which returns a boolean value indicating whether the regular expression is in dotAll mode.
ES Getting Started - Modifiers: dotAll Mode
RegExp named capture groups
ES2018 introduces Named Capture Groups, which allows you to assign a name to each group match, making it easier to read the code and easier to reference.
const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; const matchObj = RE_DATE.exec("1999-12-31"); const year = matchObj.groups.year; // "1999" const month = matchObj.groups.month; // "12" const day = matchObj.groups.day; // "31"
ES Getting Started - Modifiers: Named Group Matching
Rest/Spread Properties.
ES6 introduced the spread operator for arrays,
In ES2018, this notation was also introduced for objects
const obj = { a: "a", b: "b", c: "c", d: "d", e: "e" }; // object structure const { a, b, c, ...rest } = obj; // make up new objects const newObj = { a, ...rest };
Regular line-behind assertions (RegExp Lookbehind Assertions.)
ES2018 introduces lookbehind assertions
"Last line assertion" means that x matches only when it is not after y, and must be written as / (? <! Y) x/ For example, only matching numbers that are not after the dollar sign should be written as / (? <! \ $) \d+/
/(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill') // ["100"] /(?<!\$)\d+/.exec('it's is worth about €90') // ["90"]
Use lookbehind assertions for string replacement.
const RE_DOLLAR_PREFIX = /(?<=\$)foo/g; "$foo %foo foo".replace(RE_DOLLAR_PREFIX, "bar"); // '$bar %foo foo'
ES Getting Started - Lookbehind Assertions
Unicode Property Escapes (RegExp Unicode Property Escapes)
ES2018 introduces a new class notation \p{...} and \P{...} that allow regular expressions to match all characters that conform to a property of Unicode.
const regexGreekSymbol = /\p{Script=Greek}/u; regexGreekSymbol.test("π"); // true // match all spaces const reg = /\p{White_Space}/; // matches all arrow characters const regexArrows = /^\p{Block=Arrows}+$/u; regexArrows.test("←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩"); // true
ES Getting Started - Unicode Property Classes
Promise.prototype.finally.
The finally() method is used to specify an action that will be performed regardless of the final state of the Promise object. This method is standard introduced in ES2018.
promise .then(result => {···}) .catch(error => {···}) .finally(() => {···});
In the above code, regardless of the final state of promise, after the callback function specified by then or catch is executed, the callback function specified by the finally method will be executed.
Completing asynchronous operations in sequence (Asynchronous Iteration)
In actual development, a set of asynchronous operations are often encountered, which need to be completed in sequence. For example, remotely read a set of URL s in turn, and then output the results in the order they were read.
async function logInOrder(urls) { // Read remote URL concurrently const textPromises = urls.map(async (url) => { const response = await fetch(url); return response.text(); }); // output in order for (const textPromise of textPromises) { console.log(await textPromise); } }
async function getData() { const promises = [fetch("url1"), fetch("url2"), fetch("url3"), fetch("url4")]; for (const item of promises) { // print out the promise console.log(item); } for await (const item of promises) { // print out the result of the request console.log(item); } }
ES Getting Started - Sequential Asynchronous Operations
ES2017-ES8
Object.values/Object.entries
The Object.values method returns an array whose members are the keys of all enumerable properties of the parameter object itself (excluding inherited).
const obj = { foo: "bar", baz: 42 }; Object.values(obj); // ["bar", 42] const obj = { 100: "a", 2: "b", 7: "c" }; Object.values(obj); // ["b", "c", "a"]
The Object.entries method returns an array of key-value pairs of all enumerable properties of the parameter object itself (excluding inherited).
const obj = { foo: "bar", baz: 42 }; Object.entries(obj); // [ ["foo", "bar"], ["baz", 42] ]
The basic purpose of Object.entries is to iterate over the properties of an object.
let obj = { one: 1, two: 2 }; for (let [k, v] of Object.entries(obj)) { console.log(`${JSON.stringify(k)}: ${JSON.stringify(v)}`); } // "one": 1 // "two": 2
Another use of the Object.entries method is to convert the object into a real Map structure.
const obj = { foo: "bar", baz: 42 }; const map = new Map(Object.entries(obj)); map; // Map { foo: "bar", baz: 42 }
String padding
ES2017 introduced the ability to complete the length of strings. If a string is not long enough, it will be filled at the head or tail. padStart() is used for head completion and padEnd() is used for tail completion.
"x".padStart(5, "ab"); // 'ababx' "x".padStart(4, "ab"); // 'abax' "x".padEnd(5, "ab"); // 'xabab' "x".padEnd(4, "ab"); // 'xaba'
A common use of padStart() is to specify the number of digits for numeric padding. The following code generates a 10-digit numeric string.
"1".padStart(10, "0"); // "0000000001" "12".padStart(10, "0"); // "0000000012" "123456".padStart(10, "0"); // "0000123456"
Another use is to prompt for string formatting.
"12".padStart(10, "YYYY-MM-DD"); // "YYYY-MM-12" "09-12".padStart(10, "YYYY-MM-DD"); // "YYYY-09-12"
Object.getOwnPropertyDescriptors
ES2017 introduces the Object.getOwnPropertyDescriptors() method, which returns the description object of all its own properties (non-inherited properties) of the specified object.
- value — the actual value of the property
- writable — whether the value of the property can be modified
- get — get function, called when a property is read
- set — set function, called when the property is written
- configurable — whether the property can be deleted and redefined via delete, its properties can be modified, and whether it can be changed to an accessor property
- enumerable — whether the property can be returned by a for-in loop
const obj = { foo: 123, get bar() { return "abc"; }, }; Object.getOwnPropertyDescriptors(obj); // { foo: // { value: 123, // writable: true, // enumerable: true, // configurable: true }, // bar: // { get: [Function: get bar], // set: undefined, // enumerable: true, // configurable: true } }
The purpose of this method is mainly to solve the problem that Object.assign() cannot copy the get attribute and set attribute correctly.
Another use of the Object.getOwnPropertyDescriptors() method is to use the Object.create() method to clone object properties to a new object. This is a shallow copy.
const shallowClone = (obj) => Object.create( Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj), );
For more details refer to ES Getting Started Tutorial - getOwnPropertyDescriptors
trailing comma for function arguments
ES2017 allows a trailing comma in the last parameter of a function.
Previously, neither the function definition nor the invocation allowed a comma after the last parameter.
function clownsEverywhere(param1, param2,) { /* ... */ } clownsEverywhere("foo", "bar",);
For more details refer to ES Getting Started Tutorial - Trailing Comma for Function Arguments
Async functions
The ES2017 standard introduced async functions to make asynchronous operations more convenient.
What is an async function? In a word, it is syntactic sugar for Generator functions.
function fakeRequest() { return new Promise((resolve, reject) => { setTimeout(() => { resolve("request succeeded"); }, 2000); }); } async function getData() { console.log("start"); const res = await fakeRequest(); console.log(res); console.log("end"); } getData(); /* 1.start 2.request succeeded 3.end */
Shared memory using Atomics
The Atomics object provides a set of static methods to perform atomic operations on SharedArrayBuffer and ArrayBuffer objects.
For more details refer to MDN-Atomics
ES2016-ES7
Array.prototype.includes
The Array.prototype.includes method returns a boolean value indicating whether an array contains the given value, similar to the includes method for strings.
[1, 2, 3] .includes(2) // true [(1, 2, 3)].includes(4) // false [(1, 2, NaN)].includes(NaN); // true
Exponentiation operator
// 2 square 2 ** 2; // 4 // 2 to the cube 2 ** 3; // 8
For more details refer to ES Getting Started Tutorial - Exponential Operator
ES2015-ES6
Recommend Ruan Yifeng ES Getting Started Tutorial , the Chinese document is no more detailed than him
Arrow functions (arrows)
Arrow function is a function abbreviation using = > syntax Unlike normal functions
- The this object in the function body is the object where it is defined, not the object where it is used.
- The this object's pointer is mutable, but in arrow functions, it's fixed.
- Cannot be used as a constructor, that is, cannot use the new command, otherwise an error will be thrown.
- You cannot use the arguments object, which does not exist in the function body. If you want to use it, you can use rest parameters instead.
- The yield command cannot be used, so arrow functions cannot be used as Generator functions.
var f = (v) => v; // Equivalent to var f = function (v) { return v; }; function foo() { setTimeout(() => { console.log("id:", this.id); }, 100); } var id = 21; // Arrow functions cause this to always point to the object where the function definition takes effect ({id: 42}), so 42 is printed foo.call({ id: 42 }); // id: 42 // Objects do not constitute a separate scope, making this point to the global object globalThis.s = 21; const obj = { s: 42, m: () => console.log(this.s), }; obj.m(); // 21
For more details refer to ES Getting Started Tutorial - Arrow Functions
Class (Class)
// ES5 function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return "(" + this.x + ", " + this.y + ")"; }; var p = new Point(1, 2); // ES6 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return "(" + this.x + ", " + this.y + ")"; } }
For more details refer to ES Getting Started Tutorial - Class
Object extensions (enhanced object literals)
Concise notation for properties of an object
const foo = "bar"; const method = function () { return "Hello!"; }; const filed = "name"; const baz = { foo, method, [filed]: "little king", }; // Equivalent to const baz = { foo: foo, method: function () { return "Hello!"; }, name: "little king", };
For more details refer to ES Getting Started Tutorial - Object Extension
template string
// Embed variables in strings let name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?`;
For more details refer to ES Getting Started Tutorial - String Templates
Array destructuring + spread operator
var [a] = []; a === undefined; // true var [a = 1] = []; a === 1; // true
For more details refer to ES Getting Started Tutorial - Spread Operator on Arrays
Function default parameter + rest parameter + spread operator
//If y is not passed or y===undefined, then y=12 function f(x, y = 12) { return x + y; } f(3) == 15;
function f(x, ...y) { // y is an array return x * y.length; } f(3, "hello", true) == 6;
function f(x, y, z) { return x + y + z; } // Pass each elem of array as argument f(...[1, 2, 3]) == 6;
For more details refer to ES Getting Started Tutorial - Function Default Parameters
block-scoped variables
With the introduction of let/const keywords in ES6, JS has function scope and global scope, and now JS can also have block-level scope.
function f() { { let x; { // OK, because in a new block scope const x = "sneaky"; // const defines that constants cannot be modified, so an error will be reported x = "foo"; } // x has been declared in block scope, so an error will be reported let x = "inner"; } }
For more details refer to ES Getting Started Tutorial - unicode
Traversal/Iterator + for...of(iterators + for...of)
A data structure is considered to have an iterator interface as long as the Symbol.iterator property is deployed, and its members can be iterated over with a for...of loop. That is, the for...of loop is calling the Symbol.iterator method of the data structure.
for...of is an alternative to for...in and forEach() that iterates over iterable data structures such as arrays, maps, sets and strings.
The original for...in loop in JavaScript can only obtain the key name of the object, but cannot directly obtain the key value. ES6 provides for...of loops, which allow traversal to obtain key values.
var arr = ["a", "b", "c", "d"]; for (let a in arr) { console.log(a); // 0 1 2 3 } for (let a of arr) { console.log(a); // a b c d } const str = "helloworld"; for (let a of str) { console.log(a); // h e l l o w o r l d }
For more details refer to ES Getting Started Tutorial - iterators
generators
Generators simplify the creation of iterators using function * and yield. A function declared as function * is a traverser object, that is, the Generator function is a traverser object generating function. The returned traverser object can traverse each state inside the Generator function in turn.
Generators are subtypes of iterators and thus have next and throw methods.
The yield expression is a marker to suspend execution, and the next method can resume execution
Note: With the advent of ES7, await is recommended.
function* foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for (let v of foo()) { console.log(v); } // 1 2 3 4 5
Below is an example of implementing the Fibonacci sequence using the Generator function and a for...of loop.
var fibonacci = { [Symbol.iterator]: function* () { let [prev, curr] = [0, 1]; for (;;) { yield curr; [prev, curr] = [curr, prev + curr]; } }, }; for (var n of fibonacci) { // if (n > 1000) break; console.log(n); }
As can be seen from the above code, the next method does not need to be used when using the for...of statement.
Using for...of loops, you can write methods that iterate over any object. The native JavaScript object has no iterator interface and cannot use the for...of loop. Add this interface to it through the Generator function and you can use it.
Generator (Generator) essentially inherits iterator (Iterator)
interface Generator extends Iterator { next(value?: any): IteratorResult; throw(exception: any); }
For more details refer to ES Getting Started Tutorial - iterators
Unicode
ES6 enhances the capabilities of Unicode, including
- Unicode representation of supported characters
For example, the Unicode code point of "medium" is u+4e2d, you can enter this Chinese character directly in the string, or you can enter its escaped form \u4e2d, the two are equivalent
"in" === "\u4e2d"; // true
- Regular expression using /u to match codepoints
// new RegExp behaviour, opt-in 'u' "Pyramid".match(/./u)[0].length == 2;
- Get the codepoint of a 32-bit UTF-16 character - codePointAt
"Pyramid".codePointAt(0) == 0x20bb7; let s = "Pyramid a"; for (let ch of s) { console.log(ch.codePointAt(0).toString(16)); } // 20bb7 // 61
For more details refer to ES Getting Started Tutorial - unicode
modules
At the level of language standards, ES6 implements the module function, and the implementation is quite simple. It can completely replace the CommonJS and AMD specifications and become a common module solution for browsers and servers.
export using export default or export
// math.js export const pi = 3.141593; export default function sum(x, y) { return x + y; }
import using import
// app.js import sum, { pi } from "./math"; alert("2π = " + sum(pi, pi));
For more details refer to ES Getting Started Tutorial-module
Module Loaders Rules (module loaders)
Module loader supports:
- Asynchronous loading
- The code runs in the module scope, not the global scope. Top-level variables inside the module, not visible outside.
- In modules, the this keyword at the top level returns undefined instead of pointing to window. That is, using the this keyword at the top level of a module is meaningless
//index.js const x = 1; console.log(x === window.x); //false console.log(this === undefined); // true
Using the syntax point of this equal to undefined at the top level, you can detect whether the current code is in an ES6 module.
const isNotModuleScript = this !== undefined;
For more details refer to ES Getting Started Tutorial - module-loader
import and export
Map + Set + Weakmap + Weakset
ES6 provides a new data structure Set. It is similar to an array, but the values of the members are all unique and there are no duplicate values.
// Sets var s = new Set(); s.add("hello").add("goodbye").add("hello"); s.size === 2; s.has("hello") === true;
ES6 provides the Map data structure. It is similar to an object and is also a collection of key-value pairs, but the scope of "keys" is not limited to strings, and various types of values (including objects) can be used as keys. That is to say, the Object structure provides a "string-value" correspondence, and the Map structure provides a "value-value" correspondence, which is a more complete Hash structure implementation. If you need a "key-value" data structure, Map is more suitable than Object.
// Maps var m = new Map(); m.set("hello", 42); m.set(s, 34); m.get(s) == 34;
The WeakMap structure is similar to the Map structure, and is also used to generate a collection of key-value pairs.
WeakMap differs from Map in two ways.
- WeakMap only accepts objects as keys (except null), and does not accept other types of values as keys.
- The object pointed to by the key name of the WeakMap is not included in the garbage collection mechanism.
// Weak Maps var wm = new WeakMap(); wm.set(s, { extra: 42 }); wm.size === undefined;
WeakSet structure is similar to Set, it is also a collection of unique values. However, it differs from Set in two ways.
- Members of WeakSet can only be objects, not other types of values.
- Objects in WeakSet are weak references
// Weak Sets var ws = new WeakSet(); ws.add({ data: 42 }); // Because the added object has no other references, it will not be held in the set
For more details refer to ES Getting Started Tutorial - Set and Map
proxies
Proxy is used to modify the default behavior of certain operations, which is equivalent to making changes at the language level. Can be used for action interception, logging/analysis, etc.
// proxy a plain object var target = {}; var handler = { get: function (receiver, name) { return `Hello, ${name}!`; }, }; var p = new Proxy(target, handler); // true p.world === "Hello, world!";
Here are all the "meta operations" that Proxy can proxy
var handler = { get:..., set:..., has:..., deleteProperty:..., apply:..., construct:..., getOwnPropertyDescriptor:..., defineProperty:..., getPrototypeOf:..., setPrototypeOf:..., enumerate:..., ownKeys:..., preventExtensions:..., isExtensible:... }
// Delegate a function object var target = function () { return "I am the target"; }; var handler = { apply: function (receiver, ...args) { return "I am the proxy"; }, }; var p = new Proxy(target, handler); //true p() === "I am the proxy";
For more details refer to ES Getting Started Tutorial - proxy
symbols
ES6 introduces a new primitive data type Symbol, representing a unique value
Symbol values are generated through the Symbol function. That is to say, the property name of the object can now have two types, one is the existing string, and the other is the newly added Symbol type. Any property name belonging to the Symbol type is unique and can be guaranteed not to conflict with other property names.
var MyClass = (function () { // var key = Symbol("key"); function MyClass(privateData) { this[key] = privateData; } MyClass.prototype = { doStuff: function () { this[key]; }, }; return MyClass; })(); var c = new MyClass("hello"); // true console.log(c["key"] === undefined);
When creating a Symbol, you can add a description.
const sym = Symbol("foo");
In the above code, the description of sym is the string foo.
Symbol is used as the attribute name. When traversing the object, the attribute will not appear in for in,for... In the of loop, object keys(),Object. getOwnPropertyNames(),JSON. stringify()return.
However, it is not a private property either, there is an Object.getOwnPropertySymbols() method that can get all the Symbol property names of the specified object. This method returns an array of all Symbol values used as property names for the current object.
const obj = {}; let a = Symbol("a"); let b = Symbol("b"); obj[a] = "Hello"; obj[b] = "World"; const objectSymbols = Object.getOwnPropertySymbols(obj); objectSymbols; // [Symbol(a), Symbol(b)]
For more details refer to ES Getting Started Tutorial - symbol
promises
Promise s are a library for asynchronous programming that hold the result of an event (usually an asynchronous operation) that will not end in the future. Many existing JavaScript libraries already use Promise s.
function timeout(duration = 0) { return new Promise((resolve, reject) => { setTimeout(resolve, duration); }); } var p = timeout(1000) .then(() => { return timeout(2000); }) .then(() => { throw new Error("hmm"); }) .catch((err) => { return Promise.all([timeout(100), timeout(200)]); });
For more details refer to ES Getting Started Tutorial - promise
math + number + string + array + object APIs
Added extension methods for many types, including: Math ,Array ,String ,Object
Number.EPSILON; Number.isInteger(Infinity); // false Number.isNaN("NaN"); // false Math.acosh(3); // 1.762747174039086 Math.hypot(3, 4); // 5 Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2); // 2 "abcde".includes("cd"); // true "abc".repeat(3); // "abcabcabc" Array.from(document.querySelectorAll("*")); // Returns a real Array Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior [(0, 0, 0)].fill(7, 1) // [0,7,7] [(1, 2, 3)].find((x) => x == 3) // 3 [(1, 2, 3)].findIndex((x) => x == 2) // 1 [(1, 2, 3, 4, 5)].copyWithin(3, 0) // [1, 2, 3, 1, 2] [("a", "b", "c")].entries() // iterator [0, "a"], [1,"b"], [2,"c"] [("a", "b", "c")].keys() // iterator 0, 1, 2 [("a", "b", "c")].values(); // iterator "a", "b", "c" Object.assign(Point, { origin: new Point(0, 0) });
For more details, refer to the ES introductory tutorial:
binary and octal literals
Two new representations of numbers.
- Binary: start with 0b
- Octal: start with 0o
0b111110111 === 503; // true 0o767 === 503; // true
reflect api
The reflect API exposes runtime-level meta-operations on objects.
The most important purpose is to use with Proxy to perform native behavior
Let Object operations become functional behaviors. Certain Object operations are imperative, such as name in obj and delete obj[name], while Reflect.has(obj, name) and Reflect.deleteProperty(obj, name) make them functional behaviors.
// old spelling "assign" in Object; // true // new spelling Reflect.has(Object, "assign"); // true
For more details refer to ES Getting Started Tutorial - reflect
tail calls
- Tail call: the last step of a function is to return and call another function
- Tail recursion: A function calls itself, called recursion. If it tail calls itself, it is called tail recursion.
- tail call optimization
Note that currently only Safari browser supports tail call optimization, neither Chrome nor Firefox support it. I won't delve into it here 😁
function factorial(n, acc = 1) { if (n <= 1) return acc; return factorial(n - 1, n * acc); } // Stack Overflow errors appear in most browsers, // But is safe in ES6 Safari factorial(100000);
For more details refer to ES Getting Started Tutorial - Tail Calls
Internationalize strings, numbers and dates via the Intl API
The Intl object is the namespace for the ECMAScript internationalization API, which provides language-sensitive string comparison, supports numeric formatting, and date and time formatting.
Intl.Collator object
The word collator means sorter. The Intl.Collator object is a constructor for collators that can support language-sensitive string comparisons.
- Chinese sorting
What if we want our Chinese to be sorted by initial pinyin?
At this point, you can use the simplified Chinese BCF 47 language tag string zh for sorting, the code is as follows:
var arrUsername = [ "Chen Kun", "Deng Chao", "Du Chun", "Feng Shaofeng", "Han Geng", "Hu Ge", "Huang Xiaoming", "Jia Nailiang", "Li Chen", "Li Yifeng", "Luhan", "Jing Boran", "Liu Ye", "Lu Yi", "Sun Honglei", ]; arrUsername.sort(new Intl.Collator("zh").compare); // The result is: ["Chen Kun", "Deng Chao", "Du Chun", "Feng Shaofeng", "Han Geng", "Hu Ge", "Huang Xiaoming", "Jia Nailiang", "Jing Boran", "Li Chen", "Li Yifeng" , "Liu Ye", "Lu Yi", "Lu Han", "Sun Honglei"]
Intl API details can refer to this article A complete introduction to JS Intl objects and their application in Chinese
ES2011-ES5
I believe that everyone is already familiar with ES5, so I only do a simple list, and I will not give examples.
'USE STRICT'
Earlier versions of JS allowed the use of undeclared variables. But when using the es5 "use strict" feature, an error is reported
// index.js "use strict"; // Error: a is not defined a = 22;