It is said that couples will never break up after reading these es2015+ common new features

Time:2022-6-12

Es2015 is also called ES6. The only difference is that one is named after the release year and the other is named after the version number

Since then, the organization has released a new version every year. According to this rule, es2016 = = = ES7 ES2020 === ES11

But usually I am used to calling es2015 and its subsequent versions collectively as es2015+, or esnext

Variable declaration

Es2015 adds two keywords to declare variable identifiers,letandconstBoth support block level scopes and are inaccessible until declared

All variable identifiers that do not require re assignment can be usedconstKeyword to declare

For other variables that need to be re assigned, useletKeyword, such as a loop counter

{
  const arr = ['a', 'b', 'c', 'd', 'e']
  const length = arr.length

  for (let i = 0; i < length; i++) {
    //
  }
}
//Cannot access outside block

Object literal extensions are prototype dependent

Short form of object literal and calculation properties

const foo = 1

const obj = {
  Foo, // attribute abbreviation, equivalent to foo: foo,
  bar() {
    //Method abbreviation
    console.log(this.name)
  },
  //Calculation properties
  ['na' + 'me']: 'by.Genesis',
  __ proto__:  Prototype // access prototype
}

These features can simplify the original code

__proto__Property is used to access the prototype, but it is not recommended. Instead, you should use theObject.getPrototypeOf(o)andObject.setPrototypeOf(o, proto)method

const obj = Object.create({ name: 'by.Genesis' })

//Set up a new prototype
Object.setPrototypeOf(obj, { age: 18 })

//Get prototype
Object.getPrototypeOf(obj) // { age: 18 }

Arrow function

The arrow (= >) is a function shorthand and provides some useful features

//When a function has only one parameter, the parentheses of the parameter can be omitted
;[1, 2, 3].forEach(item => { console.log(item) })

//When there is only one statement in the function body, you can omit the curly braces in the function body and implicitly return the statement
const sum = (x, y) => x + y

//If an object literal is implicitly returned, a pair of parentheses can be used to wrap the object literal in order to eliminate ambiguity
const pos = (x, y) => ({ x: x + 1, y: y * 2 })

//Morphology this
const obj = {
  name: 'by.Genesis',
  showName() {
    setTimeout(() => {
      console.log(this.name) // obj.showName() this === obj
    }, 300)
  }
}

//Execute arrow function expression now
;(() => {
  alert(101)
})()

class

Class is the traditional constructor syntax sugar based on prototype inheritance

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  update() {
    //Method
  }
}

//Inheritance
class Student extends Person {
  constructor(name, age, grade) {
    super(name, age)
    this.grade = grade
  }

  update() {
    //Call the method of the parent class
    super.update()
  }
  static baz() {
    //Static method through student Baz() call
  }
}

const s1 = new Student('by.Genesis', 20, 2)

Define getter/setter

let x = 1

class Person {
  get foo() {
    return x
  }
  set foo(v) {
    x = v
  }
}

const p = new Person()
p.foo // 1
p.foo = 10

Like functions, classes can be assigned to a variable as an expression, passed to a function as a parameter, or even returned from a function

const Person = class {
  //
}

Whether it is a class declaration or an expression, it needs to be defined first and then used. It will not be promoted

Symbol

A symbol is a new primitive type that has no literal form

Symbols can be divided into three categories: ordinary symbols, global symbols and well-known symbols

//Create symbol without new
//The passed in parameter is used as the descriptor of the symbol
const name = Symbol('name')

//Use symbol as key for object
//Only computable attribute names can be used
const o = {
  [name]: 'by.Genesis'
}

//Determine the value type through the typeof operator
typeof name === 'symbol'

//The symbol value is unique. Even if the same parameter is passed in during creation, the resulting symbol is not the same
Symbol('name') !== Symbol('name')

//However, in the global symbol registry, the same key returns the same symbol
Symbol.for('name') === Symbol.for('name')

//Get symbol descriptor
Symbol('name').description === 'name'

The symbolic attribute of an object cannot be traversed by traditional methods. It can be used when necessaryObject.getOwnPropertySymbols()Method to obtain an array of all symbol attributes in the parameter object

Object.getOwnPropertySymbols(o) // [Symbol(name)]

In addition, there are some well-known symbols, which are used to expose some JavaScript internal operations

//When the array is used as the concat parameter, it will be expanded by default
const arr = [4, 5, 6]
;[1, 2, 3].concat(arr) // [1, 2, 3, 4, 5, 6]

//You can modify this behavior so that array parameters are not expanded
arr[Symbol.isConcatSpreadable] = false
;[1, 2, 3].concat(arr) // [1, 2, 3, [4, 5, 6]]

//You can also expand a class array object
;[1, 2, 3].concat({
  [Symbol.isConcatSpreadable]: true,
  length: 3,
  0: 4,
  1: 5,
  2: 6
}) // [1, 2, 3, 4, 5, 6]

Promise

PromiseMainly used to represent a future value

//Create a promise
const p = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'by.Genesis')
})

//Execute on promise resolve
p.then(res => {
  console.log(res) // 'by.Genesis'
})

//Always execute, whether resolve or reject
p.finally(() => console.log('finally'))

//Create a fulfilled promise now
const p2 = Promise. Resolve ('101 real dog ')

//Create a rejected promise now
const p3 = Promise.reject(404)

//Execute when promise reject
p3.catch(err => {
  console.log(err) // 404
})

//Wait for a group of promise to resolve all
//Reject once one is rejected
Promise.all([p2, p3])

//Get the fastest one in a group of promises, whether resolve or reject
Promise.race([p2, p3])

//Wait for a group of promises to be all set, no matter resolve or reject
Promise.allSettled([p2, p3])

//Get the fastest resolve in a group of promises
//Reject only when all are rejected
Promise.any([p2, p3])

Iterators and generators

When an object has onenextMethod, and when the method is called, adoneandvalueThe result object of two attributes, then this is an iterator

iterator = {
  next() {
    return {
      done: false,
      value: 10
    }
  }
}

amongdoneIs a boolean type, indicating whether the iterator has been iterated

Generator is a special function, which is declared in thefunctionThere is an asterisk (*) between the keyword and the function name

Pass in generatoryieldKeyword return value

function *g() {
  yield 1
  yield 2
  yield 3
}

//Call the generator to get an iterator
iterator = g()

//Call the next method of the iterator to execute the internal code of the generator and get the result object
iterator.next() // { value: 1, done: false }

When an object has a special symbol[Symbol.iterator]Method, and when the method returns an iterator, the object is an iteratable object

iterable = {
  *[symbol.iterator] () {// object method shorthand, attribute calculation and generator are used here at the same time
    yield 1
    yield 2
    yield 3
  }
}

String, array, map, set, NodeList, etc. are all iteratable objects. The iterator itself is also an iteratable object[Symbol.iterator]Method returns itself

The generator can use theyield*Delegate to other iteratible objects

iterable = {
  *[Symbol.iterator]() {
    yield 1
    yield* [2, 3]
  }
}

Iteratible objects can usefor ofSyntax traversal

for (let v of iterable) {
  console.log(v) // 1 2 3
}

Asynchronous function

Async function add a function before the functionasyncKeyword, which can be used internallyawaitkeyword

awaitThe expression can extract the promise resolve value after it

async function fn() {
  const x = await Promise.resolve(101)
  return x
}

//Executing an asynchronous function also returns a promise
fn().then(res => console.log(res)) // 101

Asynchronous functions are generators and promise syntax sugar

Asynchronous iteration

When an iteratornextMethod returns a promise, and after the promise resolves, you can get adoneandvalueThe result object of two attributes, then this iterator is an asynchronous iterator

asyncIterator = {
  next() {
    return Promise.resolve({
      done: false,
      value: 10
    })
  }
}

The combination of asynchronous functions and generators is the asynchronous generator, which can be used internally at the same timeawaitandyieldkeyword

async function *g() {
  yield 1
  const a = await new Promise(resolve => {
    setTimeout(resolve, 3000, 2)
  })
  yield a
  yield Promise.resolve(a + 1)
}

//Executing an asynchronous generator returns an asynchronous iterator
asyncIterator = g()

When an object has a special symbol[Symbol.asyncIterator]Method, and when the method returns an asynchronous iterator, the object is an asynchronous iteratable object

asyncIterable = {
  async *[Symbol.asyncIterator]() {
    yield 1
    const a = await new Promise(resolve => {
      setTimeout(resolve, 3000, 2)
    })
    yield a
    yield Promise.resolve(a + 1)
  }
}

Asynchronous iteratable object usagefor await ofSyntax traversal

;(async () => {
  for await (let v of asyncIterable) {
    console.log(v) // 1 2 3
  }
})()

awaitShould be placed in an asynchronous function

Map & Set

Map is an ordered set containing key value pairs, where key can beAny type(any type, including reference type and even DOM element, can be used as the key of map)

//Create a map
const m = new Map([['a', 1], ['b', 2]])

//Add a value. If it already exists, modify it
m.set('c', 3)

//Get value
m.get('b') // 2

//Judgment value
m.has('b') // true

//Get length
m.size // 3

//Delete value
m.delete('b')
m.has('b') // false
m.size // 2

//Empty
m.clear()
m.size // 0

A set is an ordered set of non repeating values

//Create a set
const s = new Set([1, 2])

//Add value
s.add(3)
s. Add (1) // the value already exists and the collection remains unchanged

//Except that there is no method to obtain the value, the rest is consistent with the map

Both map and set are iteratable objects, which canforEachMethod, or you can use thefor ofgrammar

s.forEach(v => {
  console.log(v)
})

for (let [key, val] of m) {
  console.log(key, val)
}

You can think of set as a special map where key and value are the same value, or you can think of set as only key

The traversal order of map and set is the same as that of adding, so they are ordered sets

WeakSet & WeakMap

Weak versions can only be used to store reference types

Weakmap only has type requirements for its key, while value can be any type

The weak version is not an iteratable object. It cannot be traversed, has no size attribute, and cannot empty the collection with the clear method. It only has the most basic methods such as adding and deleting

Weak versions are weak references, and their advantage is to facilitate garbage collection

deconstruction

Extract values from objects or iteratable objects according to a certain pattern

//Iterative object deconstruction
//Let declaration is valid for variables a, B and C
let [a, b, c] = [1, 2]
a === 1
b === 2
c === undefined

//Exchange value
;[a, b] = [b, a] // a = 2, b = 1

//Arrays can deconstruct any iteratable object, including strings
//You can also skip some unnecessary values during deconstruction
;[, , c] = '123' // c = '3'

//Object attribute deconstruction
{ x, y, z: { w } } = { x: 3, y: 4, z: { w: 5 } }
x === 3
y === 4
w === 5

Default

You can specify a default value when declaring a function parameter or when deconstructing. When the corresponding value isundefinedThis default value will be used when

//Function parameter defaults
const sum = (x, y = 4) => x + y
sum(3) === 7

//Default value for iterator deconstruction
const [a, b = 2] = [1]
a === 1
b === 2

//Default values for object deconstruction
const { name = 'by.Genesis' } = { age: 18 }
name === 'by.Genesis'

//Function arguments are used with object deconstruction
const fn = ({ height = 18, width = 36 } = {}) => {}
fn() // height = 18, width = 36
fn({ height: 36 }) // height = 36, width = 36
fn({ height: 36, width: 18 }) // height = 36, width = 18

Spread & Rest

An iteratable object can use the spread operator (…) Expand to independent values, which can be used as parameters of a function or placed in an array

//Expand the iteratable object as a function parameter
Math.max(...[5, 20, 10]) === 20

//Expand iteratible objects into an array
const arr = [...new Set([1, 2, 2, 3])] // [1, 2, 3]

//Expand iteratible objects into an array
const newArr = [1, ...[2, 3], ...'45'] // [1, 2, 3, '4', '5']

Ordinary objects can also expand their attributes and put them into another object, which is similar toObject.assignThe method works similarly

//Expand object properties to another object
const o = {
  a: 1,
  ...{
    b: 2,
    c: 3
  }
} // o = { a: 1, b: 2, c: 3 }

const o2 = Object.assign({ a: 1 }, { b: 2, c: 3 })

Instead of expanding, multiple values can use the collect (rest) operator (…) Package into an array, or package multiple object attributes into an object

//The remaining parameters of the function are packed into an array
const fn = (x, ...y) => y.length
fn(2, 5, 7, 11) === 3 // x = 2, y = [5, 7, 11]

//The remaining values of the iteratable object are packed into an array
const [a, ...b] = new Set([2, 5, 7, 11])
a === 2
// b = [5, 7, 11]

//The remaining attributes of the object are packaged into one object
const { a, ...o } = {
  a: 1,
  b: 2,
  c: 3
} // o = { b: 2, c: 3 }

The collection operator can only be used for the last identifier

Template string

Template string is a more powerful string, which supports multiple lines and interpolation

Use interpolation in template string${}An expression can be inserted into curly braces, and the expression can even be another template string

const str = `

  ${lists.map(item => {
    return `${item.user} is ${item.age} years old.`
  }).join('')}

`

Label template

const username = 'by.Genesis'
const age = 18
const str = tag`${username} is ${age} years old.`

//Tag is a function
//The first parameter is an array of interpolated strings
//The following parameters are the values of interpolation expressions
//You can handle string logic yourself
function tag(template, ...substitutions) {
  console.log(template) // ['', ' is ', ' years old.']
  console.log(substitutions) // ['by.Genesis', 18]
  return substitutions[0] + template[1] + 'handsome'
}

str === 'by.Genesis is handsome'

Proxy and reflection

Proxy is to generate a proxy for a target object. When some operations are performed on this proxy object, the corresponding interceptor will be triggered. In the interceptor, you can define the operation and the returned value, or use reflection to perform meta operations. Each proxy method has a corresponding reflection method

const obj = {}
const proxy = new Proxy(obj, {
  get(target, key) {
    //Attribute value
    if (key === 'name') {
      //Custom return value
      return 'by.Genesis'
    } else {
      //Restore operation with reflection
      return Reflect.get(target, key)
    }
  },
  Set() {attribute assignment},
  Has() {in operator},
  Deleteproperty() {delete property},
  Getprototypeof() {get prototype},
  Setprototypeof() {set prototype},
  defineProperty() { Object.defineProperty },
  getOwnPropertyDescriptor() { Object.getOwnPropertyDescriptor },
  preventExtensions() { Object.preventExtensions },
  isExtensible() { Object.isExtensible },
  ownKeys() { Object.keys, Object.getOwnPropertyNames, Object.getOwnPropertySymbols, Object.assign },
  Enumerable() {for in loop},
  Apply() {normal function call},
  Construct() {call function in new mode}
})
proxy.name === 'by.Genesis'
obj.name === undefined

The above are these interceptors and their corresponding trigger conditions

Logical operation

Nullish coalescing Operator

The false values in JavaScript havenull, undefined, 0, '', NaN, false, true except false

The null value has onlynullandundefinedWhen the left side of the operator is null, the right side is returned

null ?? 1 // 1
undefined ?? 1 // 1

0 ?? 1 // 0
0 || 1 // 1

Optional chaining

In chain operation, when a value in the middle isnullperhapsundefinedAn error will be reported, and this operator can make the chain operation safer

const o = {}
o.p.q // Uncaught TypeError: Cannot read property 'q' of undefined
o.p?.q // undefined

Logical assignment

//Logical or assignment
A | = B // assign value when a is false
a || a = b

//Logic and assignment
A & & = B // assign value when a is true
a && a = b

//Logical null assignment
a ??=  B // assign value when a is null
a ?? a = b

modular

Before the advent of ES modules, there were various specifications for defining modules, such as AMD and commonjs. Es modules provide language level support

useexportKeyword export module, usingimportKeyword import module

//Multiple named modules can be exported at the same time
export const sum = (x, y) => x + y
export const name = 'by.Genesis'

//When importing a named module, the name must be the same as when exporting
//Alternatively, you can use the as keyword to specify an alias
import { sum, name as username } from './example.js'

//You can also declare before exporting. You can also use the as keyword to specify an alias when exporting
//After you specify an alias, you need to use it when importing
export { sum, name as username }

//Only one default export is allowed for a module
export default { name: 'by.Genesis' }

//The default module imported can be named arbitrarily
import o from './example.js'

//Import both default and named modules
import o, { sum } from './example.js'

//Import all and specify an alias. All modules will become the properties of this alias
import * as m from './example.js'
m. Default // the default module is the default attribute
m. Sum // a named module is its own name

//Import and export from another module
export * from './another.js'

//Import directly without specifying any name
import './example.js'

//You can even not export anything, just execute some code

number

//Non infinite
Number.isFinite(101) // true
Number.isFinite(NaN) // false

//Safe integer
Number.isSafeInteger(Number.MAX_SAFE_INTEGER) === true

//Binary digit 0b, octal digit 0o
//The number zero in the front and the letter in the back can be used in both case, but it is recommended to use lowercase for easy distinction
0b1001 === 0o11

//The new exponential operator (two multiplication signs) is mainly used to provide a serious operator for exponential operation, rather than calling methods
2 ** 3 === Math.pow(2, 3)
2 ** 3 === 2 * 2 * 2

//Underline can be added at any position in the number to divide the number and increase the readability of the number
123_ 4567_ 8889 = = = 1234567889 // 123billion 456700008889
1_ 000_ 000 = = = 1000000 // onemillion

BigInt

Large integers are used to represent integers outside the range of safe integers. An n is added after the numeric literal

const num = 9007199254740992n
typeof num === 'bigint'

String method

//Repeat several times
'xyz'.repeat(3) // 'xyzxyzxyz'

//Judge the beginning
' https://imgs.developpaper.com/imgs/ Judge end'avatar jpg'. endsWith('.jpg') === true

//Judge include
'xyz'.includes('yz') === true

//The first parameter is the length after filling, and the second parameter is the filling string
'2'.padStart(2, '0') // '02'
//If the string has reached the length, it will not be filled
'12'.padStart(2, '0') // '12'

//Head and tail de blank
'  xyz  '.trimStart() === 'xyz  '
'  xyz  '.trimLeft() === 'xyz  '

'  xyz  '.trimEnd() === '  xyz'
'  xyz  '.trimRight() === '  xyz'

//Replace all strings
'xyx'.replaceAll('x', 'z') === 'zyz'
//The replace method will only replace once
'xyx'.replace('x', 'z') === 'zyx'
//Multiple substitutions require global regularization
'xyx'.replace(/x/g, 'z') === 'zyz'

Array method

Static method

//Create an array with only one numeric value
Array.of(3) // [3]
//Constructor will only create a sparse array with the length of the passed in number
new Array(3) // [empty × 3]

//Convert a class array or an iteratable object to an array
Array.from($('.modal'))
Array.from({
  length: 5
}).map((item, index) => index + 1) // [1, 2, 3, 4, 5]

Instance method

//Fill the array. You can pass in a starting index
new Array(3).fill('x', 1) // [empty, 'x', 'x']

//Include judgment. You can pass in a starting index
[NaN, 1, 2].includes(NaN) === true
[NaN, 1, 2].includes(NaN, 1) === false

//Find elements in the array and return the found elements
[1, 2, NaN].find(item => item !== item) // NaN

//Find the element index and return the index of the found element
['x', 'y', NaN].findIndex(item => item !== item) === 2

//Copy to specified location
//This is a mutation method, which can be modified directly on the original array
// Array#copyWithin(target, start, ?end)
[1, 2, 3, 4, 5, 6].copyWithin(3, 0, 3) // [1, 2, 3, 1, 2, 3]

//Flattening
[1, [2, [3, [4]]]].flat(2) // [1, 2, 3, [4]]
//How many floors are there? That's infinite
[1, [2, [3, [4]]]].flat(Infinity) // [1, 2, 3, 4]

//Flatmap is equivalent to map + flat (1)
//Will automatically flatten one layer
//This method can make the returned array longer, which is not reasonable for ordinary maps
[1, 2, 3, 4].flatMap(x => [x, x * x]) // [1, 1, 2, 4, 3, 9, 4, 16]

Other methods

//Object comparison
Object.is(NaN, NaN) // true
Object.is(0, -0) // false

// Object. Keys() supplementary method
Object.values({ x: 1, y: 2 }) // [1, 2]
Object.entries({ x: 1, y: 2 }) // [['x', 1], ['y', 2]]
Object.fromEntries([['x', 1], ['y', 2]]) // { x: 1, y: 2 }

//Get all self attribute descriptions of the object
Object.getOwnPropertyDescriptors({ x: 1, y: 2 }) // { x: { value: 1, writable: true, enumerable: true, configurable: true }, y: { value: 2, ... } }