What do ES5/ES6/ES7/ES8/ES9/ES10/ES11/ES12 have respectively

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.

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.

ES Getting Started - finally

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

  1. 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.
  1. Cannot be used as a constructor, that is, cannot use the new command, otherwise an error will be thrown.
  2. 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.
  3. 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.

  1. WeakMap only accepts objects as keys (except null), and does not accept other types of values as keys.
  2. 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.

  1. Members of WeakSet can only be objects, not other types of values.
  2. 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:...
}

MDN-handler.get()

// 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;

Array

Array.isArray

Array.forEach

Array.map

Array.filter

Array.reduce

Array.reduceRight

Array.every

Array.some

Array.indexOf

Array.lastIndexOf

JSON

JSON.parse

JSON.stringify

DATE

Date.now()

Date.now().valueOf()

Object.defineProperty()

Reference documentation

  1. ECMAScript 6 Features
  2. es6-features.org
  3. ES2021 Features with simple examples
  4. 4 powerful JavaScript operators
  5. ES6 core features

Tags: Javascript

Posted by FadeOut79 on Thu, 28 Jul 2022 03:08:56 +0930