Statement:
- This article is reproduced from: Talking about the new functions of ECMAScript(ES6-ES11) from the development history of JavaScript
- I made a small typesetting modification to the original text. Thank you very much for your enthusiastic sharing~
preface
JavaScript is the most widely used and best developed front-end and back-end language (the back-end is mainly Nodejs). If we want to use JavaScript flexibly, we first need to understand the basic knowledge and development process of JavaScript and ECMAScript(ES).
1, The birth of JavaScript
JavaScript came into being because of the Internet and followed the emergence of browsers. Reviewing its history, we should start from the history of the browser.
At the end of 1990, Tim Berners Lee, a scientist of CERN, invented the World Wide Web based on the Internet, the world's largest computer network. From then on, you can browse web documents on the Internet. The earliest web pages can only be viewed in the terminal of the operating system, that is, they can only be operated by using the command line. All web pages are displayed in the character window, which is of course very inconvenient.
At the end of 1992, the national supercomputer Application Center (NCSA) began to develop an independent browser called Mosaic. This is the first browser in human history. From then on, the web page can be browsed in the window of graphical interface.
In October 1994, Marc Andreessen, a major programmer of NCSA, and Jim Clark, a venture capitalist, established Mosaic Communications, which was later renamed Netscape. This is the new development direction of Netscape based on the common browser.
In December 1994, Navigator released version 1.0, with a market share of more than 90%.
Netscape soon found that the Navigator browser needed a scripting language that could be embedded in web pages to control browser behavior. At that time, the network speed was very slow and the Internet fee was very expensive. Some operations should not be completed on the server side. For example, if the user forgets to fill in the "user name", click the "send" button. It's a little too late to find this on the server. It's best to tell the user "please fill in the user name" before the user sends the data. This requires embedding a small program in the web page to let the browser check whether each column is filled in.
The management's assumption of this browser script language is that the function does not need to be too strong, the syntax is relatively simple, and it is easy to learn and deploy. That year, Sun's Java language came out, and the marketing campaign was very successful. Netscape decided to work with Sun to support embedded Java applets (later called Java applets). However, whether the browser scripting language uses Java is controversial. Later, I decided not to use Java, because Web applet doesn't need Java's "heavy" syntax. However, it also determines that the syntax of the scripting language should be close to Java and can support Java programs. These assumptions directly exclude the use of existing languages, such as Perl, Python, and TCL.
In 1995, Netscape hired programmer Brendan Eich to develop the web scripting language. Brendan Eich has a strong background in functional programming and hopes to implement this new language based on Scheme language (a dialect of LISP language, the ancestor of functional language).
In May 1995, Brendan Eich completed the first version of the language in only 10 days. It is a hodgepodge of grammar from many sources.
- Basic syntax: learn from C language and Java language.
- Data structure: learn from Java language, including dividing values into original values and objects.
- Usage of functions: using Scheme language and Awk language for reference, take functions as first-class citizens and introduce closures.
- Prototype inheritance model: learn from Self language (a variant of Smalltalk).
- Regular expression: learn from Perl language.
- String and array processing: learn from Python language.
In order to keep it simple, this scripting language lacks some key functions, such as block level scope, module, subtype, etc., but existing functions can be used to find solutions. This lack of function directly led to a significant feature of JavaScript later: for other languages, you need to learn various functions of the language, while for JavaScript, you often need to learn various problem-solving modes. Moreover, due to the variety of sources, it is doomed from the beginning that the programming style of JavaScript is a mixture of functional programming and object-oriented programming.
Netscape's browser scripting language, originally called Mocha, was changed to LiveScript in September 1995. In December, Netscape reached an agreement with Sun, the inventor and owner of the Java language, which allows the language to be called JavaScript. In this way, with the help of the company's Java browser, the company can expand its influence to Netscape.
The reason for this name is not that JavaScript itself has a deep relationship with the Java language (in fact, the relationship between the two is not deep, see the next section for details), but because Netscape has decided to use the Java language to develop network applications, and JavaScript can connect all parts like glue. Of course, the later history is that the browser plug-in of Java language failed, and JavaScript flourished instead.
On December 4, 1995, Netscape and Sun jointly released the JavaScript language, publicizing that JavaScript is a supplement to Java and belongs to lightweight Java, which is specially used to operate web pages.
In March 1996, Navigator 2.0 browser officially built-in JavaScript scripting language.
2, Relationship between JavaScript and ECMAScript
To clarify this issue, we need to review history. In November 1996, Netscape, the creator of JavaScript, decided to submit JavaScript to the international organization for standardization ECMA, hoping that this language can become an international standard. The next year, ECMA released the first version of standard document No. 262 (ECMA-262), which stipulated the standard of browser scripting language and called this language ECMAScript. This version is version 1.0.
The standard was formulated for JavaScript language from the beginning, but it is not called JavaScript for two reasons. First, trademark. Java is a trademark of Sun company. According to the license agreement, only Netscape company can legally use the name JavaScript, and JavaScript itself has been registered as a trademark by Netscape company. Second, we want to show that the language is formulated by ECMA, not Netscape, which is conducive to ensuring the openness and neutrality of the language.
Therefore, the relationship between ECMAScript and JavaScript is that ECMAScript is a simple JavaScript Standard Specification, and JavaScript is an implementation of ECMAScript (other ECMAScript dialects include JScript and ActionScript). Moreover, ECMAScript continues to add new functions to JavaScript.
From the release of ECMAScript 1.0 in July 1997 to now, ECMAScript has officially released 11 versions. Next, we mainly introduce the new functions of each version from ES6 (ES2015) to ES11 (latest ES2020).
3, New features of ES6 (2015)
ES6 has many features, and it was not standardized until nearly six years after the release of ES5 (from November 2009 to June 2015). There is a large time span between the two releases, so there are many features in ES6. Here are some common:
- class
- modularization
- Arrow function
- Function parameter defaults
- Template string
- Destructuring assignment
- Extension operator
- Object attribute abbreviation
- Promise
- Const
1. class
For developers who are familiar with Java, object-c, c# and other pure object-oriented languages, they will have a special feeling for class. ES6 introduces class, which makes object-oriented programming of JavaScript easier and easier to understand.
class Animal { constructor(name, color) { this.name = name this.color = color } toString() { console.log('name:' + this.name + ',color:' + this.color) } } var animal = new Animal('dog', 'white') animal.toString() console.log(animal.hasOwnProperty('name')) console.log(animal.hasOwnProperty('toString')) console.log(animal.__proto__.hasOwnProperty('toString')) class Cat extends Animal { constructor(action) { // If there is no top-level constructor, the default constructor with super function will be added super('cat', 'white') this.action = action } toString() { console.log(super.toString()) } } var cat = new Cat('catch') cat.toString() console.log(cat instanceof Cat) console.log(cat instanceof Animal)
2. Modularization
ES5 supports native modularization, and modules are added as an important part in ES6. The function of the module is mainly composed of export and import. Each module has its own scope. The mutual calling relationship between modules specifies the exposed interfaces of the module through export, and references the interfaces provided by other modules through import. At the same time, a namespace is created for the module to prevent the naming conflict of functions.
Export
ES6 allows you to use export in a module to export multiple variables or functions.
Export variables
export var name = 'Rainbow'
Experience: ES6 supports not only the export of variables, but also the export of constants. export const sqrt =Math.sqrt;// Export constants
ES6 regards a file as a module, and the above module outputs a variable through export. A module can also output multiple variables to the outside at the same time.
var name = 'Rainbow' var age = '24' export { name, age }
derived function
export function myModule(someArg) { return someArg }
Import
After defining the output of a module, it can be referenced in another module through import.
import { myModule } from 'myModule' import { name, age } from 'test'
Experience: an import statement can import default functions and other variables at the same time. importdefaultMethod, { otherMethod } from 'xxx.js';
3. Arrow function
This is one of the most exciting features in ES6. = > Not only is the keyword function abbreviated, it also brings other benefits. The arrow function and the code surrounding it share the same this, which can help you solve the pointing problem of this. Experienced JavaScript developers are familiar with things like var self = this; Or var that =this refers to the mode of peripheral this. But with = >, this mode is not needed.
Structure of arrow function
The arrow = > of the arrow function is preceded by an empty parenthesis, a single parameter name, or multiple parameter names enclosed in parentheses, and the arrow can be followed by an expression (as the return value of the function) or a function body enclosed in curly braces (you need to return the value through return, otherwise it is undefined).
() => 1 v => v+1 (a,b) => a+b () => { alert("foo"); } e => { if (e == 0){ return 0; } return 1000/e; }
Experience: no matter the arrow function or bind, every time it is executed, a new function reference is returned. Therefore, if you need the function reference to do something else (such as unloading the listener), you must save the reference yourself.
Traps when uninstalling listeners
Wrong practice
class PauseMenu extends React.Component { componentWillMount() { AppStateIOS.addEventListener('change', this.onAppPaused.bind(this)) } componentWillUnmount() { AppStateIOS.removeEventListener('change', this.onAppPaused.bind(this)) } onAppPaused(event) {} }
Right approach
class PauseMenu extends React.Component { constructor(props) { super(props) this._onAppPaused = this.onAppPaused.bind(this) } componentWillMount() { AppStateIOS.addEventListener('change', this._onAppPaused) } componentWillUnmount() { AppStateIOS.removeEventListener('change', this._onAppPaused) } onAppPaused(event) {} }
In addition to the above, we can also do this:
class PauseMenu extends React.Component { componentWillMount() { AppStateIOS.addEventListener('change', this.onAppPaused) } componentWillUnmount() { AppStateIOS.removeEventListener('change', this.onAppPaused) } onAppPaused = (event) => {} }
It should be noted that no matter bind or arrow function, a new function reference is returned every time it is executed. Therefore, if you need the function reference to do something else (such as unloading the listener), you must save the reference yourself.
ES6 supports setting default values for functions when they are defined:
function foo(height = 50, color = 'red') {}
Do not use default values:
function foo(height, color) { var height = height || 50 var color = color || 'red' }
This is generally no problem, but it will be a problem when the Boolean value of the parameter is false. For example, we call foo function as follows:
foo(0, '')
Because the Boolean value of 0 is false, the value of height will be 50. Similarly, the value of color is' red '.
Therefore, the default value of function parameters can not only make the code more concise, but also avoid some problems.
5. Template string
ES6 supports Template Strings, making string splicing more concise and intuitive.
Do not use template string:
var name = 'Your name is ' + first + ' ' + last + '.'
Use template string:
var name = `Your name is ${first} ${last}.`
In ES6, ${} can be used to splice strings. You only need to put variables in braces.
Deconstruction assignment syntax is an expression of JavaScript, which can quickly extract values from arrays or objects and assign them to defined variables.
Gets the value in the array
Get values from the array and assign them to variables. The order of variables corresponds to the order of objects in the array.
var foo = ['one', 'two', 'three', 'four'] var [one, two, three] = foo console.log(one) console.log(two) console.log(three) //If you want to ignore some values, you can get the value you want according to the following writing var [first, , , last] = foo console.log(first) console.log(last) //You can write that, too var a, b; [a, b] = [1, 2] console.log(a) console.log(b)
If you don't get a value from the in the array, you can set a default value for the variable.
var a, b; [a = 5, b = 7] = [1] console.log(a) console.log(b)
The values of two variables can be easily exchanged through deconstruction and assignment.
var a = 1 var b = 3 ;[a, b] = [b, a] console.log(a) console.log(b)
Gets the value in the object
const student = { name: 'Ming', age: '18', city: 'Shanghai', } const { name, age, city } = student console.log(name) console.log(age) console.log(city)
7. Spread operator
Extension operator You can expand the array expression or string at the syntax level during function call / array construction; You can also expand the object expression by key value when constructing an object.
grammar
Function call:
myFunction(...iterableObj)
Array construction or string:
;[...iterableObj, '4', ...'hello', 6]
When constructing objects, clone or copy attributes (a new feature of ECMAScript 2018 specification):
let objClone = { ...obj }
Application scenario
Use the extension operator when calling a function
function sum(x, y, z) { return x + y + z } const numbers = [1, 2, 3] console.log(sum.apply(null, numbers)) console.log(sum(...numbers))
Construct array
When there is no expansion syntax, you can only combine push, splice, concat and other methods to turn existing array elements into part of a new array. With the expansion syntax, constructing a new array becomes simpler and more elegant:
const stuendts = ['Jine', 'Tom'] const persons = ['Tony', ...stuendts, 'Aaron', 'Anna'] conslog.log(persions)
Similar to the expansion of parameter list When constructing a word number group, it can be used multiple times at any position.
Array copy
var arr = [1, 2, 3] var arr2 = [...arr] arr2.push(4) console.log(arr2)
Expand syntax and object The behavior of assign () is consistent, and all of them are shallow copies (only one layer is traversed).
Connect multiple arrays
var arr1 = [0, 1, 2] var arr2 = [3, 4, 5] var arr3 = [...arr1, ...arr2] //Equivalent to var arr4 = arr1.concat(arr2)
In ECMAScript 2018, the extension operator adds support for objects
var obj1 = { foo: 'bar', x: 42 } var obj2 = { foo: 'baz', y: 13 } var clonedObj = { ...obj1 } var mergedObj = { ...obj1, ...obj2 }
javascript
Application in React
Props is usually used to encapsulate some functions. In most cases, the transfer props that should be displayed are used externally. But when passing a large number of props, it will be very cumbersome. At this time, we can use (extension operator, which is used to get all the traversable properties of the parameter object) to pass.
In general, we should write like this
<CustomComponent name ='Jine' age ={21} />
Using... Is equivalent to the above writing
const params = { name: 'Jine', age: 21 } <CustomComponent {...params} />
Cooperate with deconstruction assignment to avoid passing in some unnecessary parameters
var params = { name: '123', title: '456', type: 'aaa' } var { type, ...other } = params; <CustomComponent type='normal' number={2} {...other} /> //Equivalent to <CustomComponent type='normal' number={2} name='123' title='456' />
8. Object attribute abbreviation
ES6 allows us to set the property of an object without specifying the property name.
Do not use ES6
const name = 'Ming', age = '18', city = 'Shanghai' const student = { name: name, age: age, city: city, } console.log(student)
The object must contain attributes and values, which is very redundant.
Use ES6
const name = 'Ming', age = '18', city = 'Shanghai' const student = { name, age, city, } console.log(student)
Object, which is very concise.
Promise is a solution for asynchronous programming, which is more elegant than the traditional solution callback. It was first proposed and implemented by the community. ES6 has written it into the language standard, unified the usage, and provided promise objects natively.
Do not use ES6
Nested two setTimeout callback functions:
setTimeout(function () { console.log('Hello') setTimeout(function () { console.log('Hi') }, 1000) }, 1000)
Use ES6
var waitSecond = new Promise(function (resolve, reject) { setTimeout(resolve, 1000) }) waitSecond .then(function () { console.log('Hello') return waitSecond }) .then(function () { console.log('Hi') })
The above code uses two then for asynchronous programming serialization to avoid callback Hell:
10. Support let and const
Previously, JS had no block level scope. const and let filled this convenient gap. const and let are both block level scopes.
Variables defined with var are function level scopes:
{ var a = 10 } console.log(a)
Variables defined with let and const are block level scopes:
{ let a = 10 } console.log(a)
4, New features of ES7 (2016)
ES2016 adds two small features to illustrate the standardization process:
- Array The includes() method is used to determine whether an array contains a specified value. According to the situation, if it does, it returns true; otherwise, it returns false.
- a ** b exponential operator, which is the same as math Pow (a, b) is the same.
1.Array.prototype.includes()
The includes() function is used to determine whether an array contains a specified value. If it does, it returns true; otherwise, it returns false.
The includes function is very similar to the indexOf function. The following two expressions are equivalent:
arr.includes(x) arr.indexOf(x) >= 0
Next, let's judge whether the number contains an element:
Practice before ES7
Use indexOf() to verify whether there is an element in the array. At this time, you need to judge whether the return value is - 1:
let arr = ['react', 'angular', 'vue'] if (arr.indexOf('react') !== -1) { console.log('react existence') }
Use the includes() of ES7
Using includes() to verify whether an element exists in the array is more intuitive and simple:
let arr = ['react', 'angular', 'vue'] if (arr.includes('react')) { console.log('react existence') }
2. Exponential operator
The exponential operator * * is introduced in ES7, which has the same function as math pow(..) Equivalent calculation results.
Do not use exponential operator
Use the custom recursive function calculateExponent or math Pow() performs exponential operation:
function calculateExponent(base, exponent) { if (exponent === 1) { return base } else { return base * calculateExponent(base, exponent - 1) } } console.log(calculateExponent(2, 10)) console.log(Math.pow(2, 10))
Use exponential operator
Use the exponential operator * *, just like the operators +, - and so on:
console.log(2 ** 10)
5, New features of ES8 (2017)
- async/await
- Object.values()
- Object.entries()
- String padding: padStart() and padEnd(), the filled string reaches the current length
- Comma is allowed at the end of function parameter list
- Object.getOwnPropertyDescriptors()
- ShareArrayBuffer and Atomics objects for reading and writing from shared memory locations
1.async/await
ES2018 introduces asynchronous iterators, which is like a regular iterator, except that the next() method returns a Promise. So await can be used with for Used together with the of loop to run asynchronous operations in a serial manner. For example:
async function process(array) { for await (let i of array) { doSomething(i) } }
2.Object.values()
Object.values() is an object Keys () is similar to the new function, but it returns all the values of the object's own attributes, excluding the inherited values.
Suppose we want to traverse all the values of obj:
const obj = { a: 1, b: 2, c: 3 }
Do not use object values() :ES7
const vals = Object.keys(obj).map((key) => obj[key]) console.log(vals)
Use object values() :ES8
const values = Object.values(obj1) console.log(values)
From the above code, we can see that object Values () saves us the steps of traversing keys and obtaining value according to these keys.
3.Object.entries()
Object. The entries() function returns an array of key value pairs of enumerable properties of a given object.
Next, let's traverse the key s and value s of all attributes of the obj object above:
Do not use object entries() :ES7
Object.keys(obj).forEach((key) => { console.log('key:' + key + ' value:' + obj[key]) }) //key:b value:2
Use object entries() :ES8
for (let [key, value] of Object.entries(obj1)) { console.log(`key: ${key} value:${value}`) } //key:b value:2
4.String padding
In ES8, two new instance functions String prototype. Padstart and String prototype. Padend, which allows you to add an empty String or other String to the beginning or end of the original String.
String.padStart(targetLength,[padString])
- targetLength: the target length that the current string needs to be filled in. If this value is less than the length of the current string, the current string itself is returned.
- padString: (optional) fill string. If the string is too long and the length of the filled string exceeds the target length, only the leftmost part will be retained, and other parts will be truncated. The default value of this parameter is "".
console.log('0.0'.padStart(4, '10')) console.log('0.0'.padStart(20))
String.padEnd(targetLength,padString])
- targetLength: the target length that the current string needs to be filled in. If this value is less than the length of the current string, the current string itself is returned.
- padString: (optional) fill string. If the string is too long and the length of the filled string exceeds the target length, only the leftmost part will be retained, and other parts will be truncated. The default value of this parameter is "";
console.log('0.0'.padEnd(4, '0')) console.log('0.0'.padEnd(10, '0'))
5. Comma is allowed at the end of function parameter list
The main function is to facilitate the modification of the same function when using git for multi person collaborative development and reduce unnecessary line changes.
6.Object.getOwnPropertyDescriptors()
Object. The getownpropertydescriptors () function is used to obtain the descriptors of all self attributes of an object. If there are no self attributes, it returns an empty object.
Function prototype:
Object.getOwnPropertyDescriptors(obj)
Returns the descriptors of all self attributes of the obj object. If there are no self attributes, it returns an empty object.
const obj2 = { name: 'Jine', get age() { return '18' }, } Object.getOwnPropertyDescriptors(obj2) // age: { // enumerable: true, // set: undefined // name: { // enumerable: true, // writable:true // }
7.SharedArrayBuffer object
SharedArrayBuffer object is used to represent a general, fixed length original binary data buffer. Similar to ArrayBuffer object, they can be used to create views on shared memory. Unlike ArrayBuffer, SharedArrayBuffer cannot be separated.
new SharedArrayBuffer(length)
8.Atomics object
The Atomics object provides a set of static methods to perform atomic operations on the SharedArrayBuffer object.
These atomic operations belong to the Atomics module. Unlike general global objects, Atomics is not a constructor, so it cannot be called with the new operator or directly as a function. All properties and methods of Atomics are static (like Math objects).
Multiple threads sharing memory can read and write data at the same location at the same time. Atomic operation will ensure that the value of the data being read or written meets the expectation, that is, the next atomic operation will not start until the end of the previous atomic operation, and its operation process will not be interrupted.
Adds the array element at the specified position to the given value, and returns the value of the element before addition.
Sums the array element at the specified position with the given value and returns the value of the element before the operation.
- Atomics.compareExchange()
If the element specified in the array is equal to the given value, it is updated to the new value and the original value of the element is returned.
Updates the element specified in the array to the given value and returns the value before the element is updated.
Returns the value of the specified element in the array.
Combines the array element at the specified position with the given value and returns the value of the element before the or operation.
Sets the element specified in the array to the given value and returns the value.
Subtracts the array element at the specified position from the given value and returns the value of the element before subtraction.
The array element at the specified position is different from the given value, and the value of the element before the XOR operation is returned.
The wait() and wake() methods adopt the futexes model (fast user space mutex) on Linux, which can make the process wait until a specific condition is true. It is mainly used to realize blocking.
Detects whether the value at a specified position in the array is still the given value. If yes, it remains suspended until it is awakened or timed out. The return value is "ok", "not equal" or "time out". When calling, if the current thread does not allow blocking, it will throw an exception (most browsers are not allowed to call wait() in the main thread).
Wakes up the thread in the waiting queue waiting on the element at the specified position of the array. The return value is the number of threads successfully awakened.
It can be used to detect whether the current system supports hardware level atomic operation. For an array of the specified size, if the current system supports hardware level atomic operations, return true; Otherwise, it means that for the array, all atomic operations in the atoms object can only be implemented with locks. This function is for technologists. – >
6, New features of ES9 (2018)
1. Asynchronous iteration
At some point in async/await, you may try to call asynchronous functions in the synchronous loop. For example:
async function process(array) { for (let i of array) { await doSomething(i) } }
This code will not work properly, nor will the following:
async function process(array) { array.forEach(async (i) => { await doSomething(i) }) }
In this code, the loop itself remains synchronized and is fully called before the internal asynchronous function.
ES2018 introduces asynchronous iterators, which is like a regular iterator, except that the next() method returns a Promise. So await can be used with for Used together with the of loop to run asynchronous operations in a serial manner. For example:
async function process(array) { for await (let i of array) { doSomething(i) } }
2.Promise.finally()
A Promise call chain either successfully reaches the last one then(), or triggered in failure catch(). In some cases, you want to run the same code whether Promise runs successfully or fails, such as clearing, deleting conversations, closing database connections, etc.
. finally() allows you to specify the final logic:
function doSomething() { doSomething1() .then(doSomething2) .then(doSomething3) .catch((err) => { console.log(err) }) .finally(() => {}) }
3.Rest/Spread attribute
ES2015 introduces rest parameters and extension operators. Three points (...) are only used for arrays. Rest parameter syntax allows us to represent an indefinite number of parameters as an array.
restParam(1, 2, 3, 4, 5) function restParam(p1, p2, ...p3) { // p2 = 2 }
The expansion operator works the opposite way, converting an array into a single argument that can be passed to a function. For example, math Max() returns the maximum value of a given number:
const values = [99, 100, -1, 48, 16] console.log(Math.max(...values)) // 100
ES2018 provides the same Rest parameter () and expansion operator as array for object deconstruction. A simple example:
const myObject = { a: 1, b: 2, c: 3, } const { a, ...x } = myObject // x = { b: 2, c: 3 }
Or you can use it to pass parameters to the function:
restParam({ a: 1, b: 2, c: 3, }) function restParam({ a, ...x }) { // x = { b: 2, c: 3 } }
Like arrays, Rest parameters can only be used at the end of a declaration. In addition, it only applies to the top level of each object, and it cannot be applied if objects are nested in objects.
Extension operators can be used within other objects, for example:
const obj1 = { a: 1, b: 2, c: 3 } const obj2 = { ...obj1, z: 26 }
You can use the extension operator to copy an object, such as obj2 = {...obj1}, but this is only A shallow copy of the object. In addition, if the attribute of an object A is object B, in the cloned object cloneB, the attribute points to object B.
4. Regular expression named capture group
JavaScript regular expressions can return a matching object - an array of classes containing matching strings, for example: parse the date in the format of YYYY-MM-DD:
const reDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/, match = reDate.exec('2018-04-30'), year = match[1], month = match[2], day = match[3]
Such code is difficult to understand, and changing the structure of regular expressions may change the index of matching objects.
ES2018 allows named capture groups to use symbols< The following example opens immediately after the capture of name >:
const reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/, match = reDate.exec('2018-04-30'), year = match.groups.year, month = match.groups.month, // 04 day = match.groups.day
Any named group that fails to match will return undefined.
Named captures can also be used in the replace() method. For example, convert the date to MM-DD-YYYY format in the United States:
const reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/, d = '2018-04-30', usDate = d.replace(reDate, '$<month>-$<day>-$<year>')
5. Regular expression reverse assertion
Currently, JavaScript supports lookahead in regular expressions. This means that the match will occur, but there will be no capture, and the assertion is not included in the entire match field. For example, capture currency symbols from prices:
const reLookahead = /\D(?=\d+)/, match = reLookahead.exec('$123.89') console.log(match[0])
ES2018 introduces a look behind that works in the same way but matches the previous one, so that I can ignore the currency symbol and simply capture the number of prices:
const reLookbehind = /(?<=\D)\d+/, match = reLookbehind.exec('$123.89') console.log(match[0])
The above is a positive reverse assertion. Non numeric \ D must exist. Similarly, there are negative reverse assertions, indicating that a value must not exist, for example:
const reLookbehindNeg = /(?<!\D)\d+/, match = reLookbehind.exec('$123.89') console.log(match[0])
6. Regular expression dotAll mode
Regular expression midpoint Match any single character except carriage return, mark s changes this behavior and allows the occurrence of line terminators, for example:
/hello.world/.test('hello\nworld'); /hello.world/s.test('hello\nworld');
7. Regular expression Unicode escape
So far, local access to Unicode character properties in regular expressions is not allowed. ES2018 adds Unicode attribute escape -- in the form of \ p {...} And \ p {...}, Use the tag u (unicode) setting in the regular expression. In the \ p block, you can set the attributes to be matched in the form of key value pairs rather than the specific content. For example:
const reGreekSymbol = /\p{Script=Greek}/u reGreekSymbol.test('π')
This feature can avoid using a specific Unicode interval to judge the content type and improve readability and maintainability.
8. Template string of non escape sequence
Before, \ u starts a unicode escape, \ x starts a hexadecimal escape, and \ followed by a number starts an octal escape. This makes it impossible to create a specific string, such as the Windows file path C:\uuu\xxx1. Refer to the template string for more details.
7, New features of ES10 (2019)
- Line separator (U + 2028) and segment separator (U + 2029) symbols are now allowed to match JSON in string text
- More friendly JSON stringify
- The flat() method and flatMap() method of Array are added
- The trimStart() method and trimEnd() method of String are added
- Object.fromEntries()
- Symbol.prototype.description
- String.prototype.matchAll
- Function.prototype.toString() now returns exact characters, including spaces and comments
- Simplify try {} catch {} and modify catch binding
- New basic data type BigInt
- globalThis
- import()
- Legacy RegEx
- Private instance methods and accessors
1. Line separator (U + 2028) and segment separator (U + 2029) symbols are now allowed to match JSON in string text
Previously, these symbols were treated as line terminators in string literals, so using them would cause SyntaxError exceptions.
2. More friendly JSON stringify
If you enter characters in Unicode format but out of range, in the original JSON Stringify returns a malformed Unicode string. Now we have implemented a change JSON Stringify's phase 3 proposal, so it outputs an escape sequence for it to make it a valid Unicode (and represented by UTF-8)
3. Add the flat() method and flatMap() method of Array
flat() and flatMap() are essentially operations of reduce and concat enate.
Array.prototype.flat()
The flat() method recursively traverses the array according to a specified depth, and combines all elements with the elements in the traversed sub array into a new array.
var arr1 = [1, 2, [3, 4]] arr1.flat() var arr2 = [1, 2, [3, 4, [5, 6]]] arr2.flat() var arr3 = [1, 2, [3, 4, [5, 6]]] arr3.flat(2) //Expand nested arrays of any depth using Infinity as the depth arr3.flat(Infinity)
- Secondly, you can also use the characteristics of the flat() method to remove the empty items of the array
var arr4 = [1, 2, , 4, 5] arr4.flat()
Array.prototype.flatMap()
The flatMap() method first uses the mapping function to map each element, and then compresses the result into a new array. It is almost the same as the flat of map and depth value 1, but flatMap is usually a little more efficient when combined into one method. Here we compare the map method with the flatMap method.
var arr1 = [1, 2, 3, 4] arr1.map((x) => [x * 2]) arr1.flatMap((x) => [x * 2]) // Only the array returned by the function in flatMap will be "flattened" one layer arr1.flatMap((x) => [[x * 2]])
4. Added trimStart() method and trimEnd() method of String
The two new methods are easy to understand. Remove the blank characters at the beginning and end of the string respectively. There is no need to declare here.
5. Object.fromEntries()
Object. The function of the entries () method is to return an array of key value pairs of enumerable attributes of a given object. Its arrangement is consistent with the order returned when traversing the object using the for... In loop (the difference is that the for in loop also enumerates the attributes in the prototype chain).
And object Fromentries() is object Inversion of entries().
Object. The fromentries () function passes in a list of key value pairs and returns a new object with these key value pairs. This iteration parameter should be an object that can implement the @ iterator method and return an iterator object. It generates an array like object with two elements. The first element is the value to be used as the attribute key, and the second element is the value associated with the attribute key.
- Through Object Fromentries to convert a Map into an Object:
const map = new Map([ ['foo', 'bar'], ['baz', 42], ]) const obj = Object.fromEntries(map) console.log(obj)
- Through Object Fromentries, which can convert Array into Object:
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'], ] const obj = Object.fromEntries(arr) console.log(obj)
6. Symbol.prototype.description
When creating a Symbol through the factory function Symbol(), you can choose to provide a string as a description through the parameter:
const sym = Symbol('The description')
Previously, the only way to access descriptions was to convert symbols to Strings:
assert.equal(sym.description, 'The description')
Getter symbol is now introduced prototype. Description to access the description directly:
assert.equal(sym.description, 'The description')
7. String.prototype.matchAll
The matchAll() method returns an iterator that contains all matching regular expressions and group capture results. Before matchAll appears, regexp. is invoked in the loop. Exec to obtain all matching item information (regexp needs to use / g flag:
const regexp = RegExp('foo*', 'g') const str = 'table football, foosball' while ((matches = regexp.exec(str)) !== null) { console.log(`Found ${matches[0]}. Next starts at ${regexp.lastIndex}.`) // expected output: "Found foo. Next starts at 19." }
If you use matchAll, you don't have to use the while loop plus exec method (and the regular expression needs to use the / g flag). Using matchAll will get the return value of an iterator, combined with for... Of, array spread, or array From() makes it easier to implement functions:
const regexp = RegExp('foo*', 'g') const str = 'table football, foosball' let matches = str.matchAll(regexp) for (const match of matches) { console.log(match) } // Array [ "foo" ] // Call matchAll again to create a new iterator matches = str.matchAll(regexp) Array.from(matches, (m) => m[0])
matchAll can be better used for grouping
var regexp = /t(e)(st(\d?))/g var str = 'test1test2' str.match(regexp) let array = [...str.matchAll(regexp)] array[0] array[1]
8. Function.prototype.toString() now returns exact characters, including spaces and comments
function /* comment */ foo /* another comment */() {} console.log(foo.toString()) // ES2019 will print the notes together console.log(foo.toString()) // Arrow function const bar = /* another comment */ () => {} console.log(bar.toString())
9. Modify the catch binding
Before ES10, we must bind exception variables for catch clauses through syntax, whether necessary or not. Many times, catch blocks are redundant. The ES10 proposal allows us to simply omit variables.
Not a big change.
Before
try {} catch (e) {}
Now it is
try {} catch {}
10. New basic data type BigInt
There are more than five basic data types (value types) (six after ES6)! Plus BigInt, there are seven basic data types: String, Number, Boolean, Null, Undefined, Symbol and BigInt
8, New features of ES11 (2020)
1.Promise.allSettled
Promise.all defect
We all know Promise All has the ability to execute asynchronous tasks concurrently. But its biggest problem is that if one of the tasks is rejected, all the tasks will hang up and Promise will directly enter the reject state.
Imagine this scenario: your page has three areas corresponding to three independent interface data, using promise All to concurrent three interfaces. If the service of any one of the interfaces is abnormal and the status is reject, the data of the three areas in the page will not be rendered, because any reject will enter the catch callback. Obviously, this is unacceptable, as follows:
Promise.all([ Promise.reject({ code: 500, msg: 'Service exception' }), Promise.resolve({ code: 200, list: [] }), Promise.resolve({ code: 200, list: [] }), ]) .then((ret) => { // If one of the tasks is reject, the callback will not be executed. RenderContent(ret) }) .catch((error) => { // This callback will be executed in this example // error: {code: 500, msg: "service exception"} })
Promise. Advantages of allsettled
We need a mechanism. If a concurrent task is normal or abnormal, it will return the corresponding state (fully or rejected) and result (business value or denial reason), and filter the desired business logic results through the filter in then, which can maximize the accessibility of the current business state, Promise Allsettled solves this problem.
Promise.allSettled([ Promise.reject({ code: 500, msg: 'Service exception' }), Promise.resolve({ code: 200, list: [] }), Promise.resolve({ code: 200, list: [] }), ]).then((ret) => { /* 0: {status: "rejected", reason: {...}} 1: {status: "fulfilled", value: {...}} 2: {status: "fulfilled", value: {...}} */ // Filter out the rejected state and ensure the data rendering of the page area as much as possible RenderContent( ret.filter((el) => { return el.status !== 'rejected' }) ) })
2. Optional chain
Optional chain allows us to query objects with multiple levels without redundant pre checks.
We often encounter this kind of query in daily development
var name = user && user.info && user.info.name
Or this
var age = user && user.info && user.info.getAge && user.info.getAge()
This is an ugly but necessary pre check, otherwise it is easy to hit Uncaught TypeError: Cannot read property This kind of error is very likely to make your whole application hang up.
With optional chain, the above code will become
var name = user?.info?.name var age = user?.info?.getAge?.()
In the optional chain? Indicates that if the expression to the left of the question mark has a value, the field after the question mark will continue to be queried. As can be seen from the above, using the optional chain can greatly simplify similar cumbersome pre verification operations, and it is safer.
3. Null value merging operator
When we query a property, we often encounter that if there is no property, a default value will be set. For example, query the player level in the following code.
var level = (user.data && user.data.level) || 'No grade'
In JS, empty string, 0, etc. will be automatically converted to false when judging by logical operators. In the above code, if the player level itself is level 0, the variable level will be assigned, and there is no level string, which is a logical error.
var level; if (typeof user.level === 'number') { level = user.level; } else if (!user.level) { level = 'No grade'; } else { level = user.level; }
Let's see how to deal with the null merge operator
// { // "level": 0 // } var level = `${user.level}level` ?? 'No grade' // Level - > 'level 0'
With null value merging operation, the code is more concise on the premise of correct logic.
The combination of null value merging operator and optional chain can easily handle multi-level queries and give default values.
var level = user.data?.level ?? 'No grade'
4.dynamic-import
The on-demand import proposal was put forward several years ago, and now it can finally enter the formal specification of ES. Personal understanding here is that "on demand" is more appropriate. M odern front-end packaging resources are becoming larger and larger, and JS resources packaged into several meters have become the norm. However, it is often not necessary to load the full amount of logic resources when the front-end application is initialized. In order to render the first screen faster, it is often loaded on demand, such as lazy loading pictures. These on-demand execution logic resources are loaded in an event callback.
el.onclick = () => { import(`/path/current-logic.js`) .then((module) => { module.doSomthing() }) .catch((err) => { // load error; }) }
Of course, this feature is well supported by webpack.
5.globalThis
JavaScript obtains global objects in different environments in different ways. NodeJS obtains global objects through global, Web through window, self, and some even through this. However, this is extremely dangerous. This is extremely complex in JavaScript, which heavily depends on the current execution context, which undoubtedly increases the complexity of obtaining global objects.
In the past, you can obtain global objects through a global function:
var getGlobal = function () { if (typeof self !== 'undefined') { return self } if (typeofwindow !== 'undefined') { returnwindow } if (typeof global !== 'undefined') { return global } thrownewError('unable to locate global object') } var globals = getGlobal() // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/globalThis
The purpose of globalThis is to provide a standardized way to access global objects. With globalThis, you can get global objects in any context and at any time.
6.BigInt
In JavaScript, the Number type can only safely represent the value in the range of - (2 ^ 53-1) to 2 ^ 53-1, that is, Number MIN_ SAFE_ Integer to Number MAX_ SAFE_ Integer, the integer calculation or representation beyond this range will lose precision.
var num = Number.MAX_SAFE_INTEGER // -> 9007199254740991 num = num + 1 // -> 9007199254740992 // After adding + 1 again, the operation cannot be normal num = num + 1 // -> 9007199254740992 // Two different values, but returned true 9007199254740992 === 9007199254740993 // -> true
To solve this problem, ES2020 provides a new data type: BigInt. There are two ways to use BigInt:
- Add n after the whole number.
var bigIntNum = 9007199254740993n
- Use the BigInt function.
var bigIntNum = BigInt(9007199254740) var anOtherBigIntNum = BigInt('9007199254740993')
Through BigInt, we can safely carry out large integer calculation.
var bigNumRet = 9007199254740993n + 9007199254740993n // -> -> 18014398509481986n bigNumRet.toString() // -> '18014398509481986'
be careful:
- BigInt is a new primitive type of data.
typeof 9007199254740993n // -> 'bigint'
- Try to avoid instantiating super large integers by calling the function BigInt. Because the literal value of the parameter is actually an instantiation of type Number, numbers beyond the safe range may cause loss of precision.
7.String.prototype.matchAll
The matchAll() method returns an iterator of all results matching a string against a regular expression, including capturing groups. -MDN
Consider the following code:
var str = '<text>JS</text><text>regular</text>' var reg = /<\w+>(.*?)<\/\w+>/g console.log(str.match(reg)) // ->["< text > JS < / text >", "< text > regular < / text >"]
It can be seen that the returned array contains the parent matching item, but it does not match the child item (group). Try removing the global search character "g".
var str = '<text>JS</text><text>regular</text>' // Note that there is no global search element identifier "g" var reg = /<\w+>(.*?)<\/\w+>/ console.log(str.match(reg)) // It will print out /* [ "<text>JS</text>", "JS", index: 0, input: "<text>JS</text><text>Regular < / text > ", groups: undefined ] */
In this way, you can get the matching parent items, including children (group s), but you can only get the first matching character. It can be seen that the above cannot match the < text > regular < / text >.
What if we get all global matches, including children?
ES2020 provides a simple way: string prototype. Matchall, this method returns an iterator.
var str = '<text>JS</text><text>regular</text>' var allMatchs = str.matchAll(/<\w+>(.*?)<\/\w+>/g) for (const match of allMatchs) { console.log(match) } /* The first iteration returns: [ "<text>JS</text>", "JS", index: 0, input: "<text>JS</text><text>Regular < / text > ", groups: undefined ] The second iteration returns: [ "<text>Regular < / text > ", "Regular ", index: 15, input: "<text>JS</text><text>Regular < / text > ", groups: undefined ] */
It can be seen that all matches can be obtained in each iteration, as well as some other meta information of the success of this match.
ES12(2021)
Reprinted from: summarizes the new features of JS syntax ES6, ES7, ES8, ES9, ES10, ES11 and ES12
1. replaceAll
Returns a new string, and all characters that match the matching rules will be replaced
const str = 'hello world'; str.replaceAll('l', ''); // "heo word"
2. Promise.any
Promise.any() receives a promise iteratable object. As long as one of the promises is successful, it will return the successful promise. If none of the promises in the iteratable object succeeds (that is, all promises fail / reject), a failed promise is returned
const promise1 = new Promise((resolve, reject) => reject('I failed Promise_1')); const promise2 = new Promise((resolve, reject) => reject('I failed Promise_2')); const promiseList = [promise1, promise2]; Promise.any(promiseList) .then(values=>{ console.log(values); }) .catch(e=>{ console.log(e); });
3. WeakRefs
Use the Class class of WeakRefs to create a weak reference to an object (a weak reference to an object means that it will not prevent the GC from recycling when the object should be recycled by the GC)
4. Logical operators and assignment expressions
Logical operators and assignment expressions. The new feature combines logical operators (& &, ||,?) And assignment expressions, and the existing composite assignment operators in JavaScript are:
a ||= b //Equivalent to a = a || (a = b) a &&= b //Equivalent to a = a && (a = b) a ??= b //Equivalent to a = a ?? (a = b)
5. Number separator
Number separator, you can create a visual separator between numbers through_ Underline to split numbers to make them more readable
const money = 1_000_000_000; //Equivalent to const money = 1000000000; 1_000_000_000 === 1000000000; // true