Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
Following a quick look at one of the ES features (ES7-ES8), today we’ll focus on the new ES feature ES9. We have already talked about the origin of ES, version corresponding rules and other contents in the last chapter, so I will not repeat them here. Let’s get right to it
A quick look at the ES features is part of my current series
- Quick introduction to one of ES features (ES7-ES8)
- Quick Introduction to ES Feature 2 (ES9)
ES2018 / ES9
Lifting Template literal restriction Lifting template literal restriction
The string template syntax is a feature of ES6 (unfortunately, I’m doing this based on the finished ES proposal, so I’m not writing about ES6, but I’ll fill it in later). In the ES6 version, we cannot insert error escape characters like \ Unicode into tagged template strings, if using such error escape characters results in bad escape sequence: \ Unicode errors. In ES9, these restrictions are lifted so that these false escapes can be executed correctly. Let’s give an example
function hi(strings) {
console.log(strings);
}
let words = hi`hi, \ustar; `;
Copy the code
Here \ustar is clearly not the correct Unicode, so we use es-check to specify ES6 to verify that this is correct
es-check es6 test.js
// SyntaxError: Bad character escape sequence (4:21)
Copy the code
It is obvious that the escape character cannot be correctly recognized as the error we mentioned above. We set the ES-Check version as ES9 this time, check again, no errors!
es-check es9 test.js
// ES-Check: there were no ES version matching errors!
Copy the code
Similar false escapes such as \x non-hexadecimal and \123 are now ok in ES9.
s (dotAll) flag for regular expressions
In regular expression pattern, we can match any single character with dot. But there are two exceptions, the default:
.
Don’t matchStar character.
Don’t matchLine terminators
The default does not match astral characters. We can solve this problem by giving the re the U (Unicode) flag. But there is no similar flag for line terminators to solve this problem. With normal regular semantics, the dot. Can match any character, but in reality only recognizes the line terminators listed below:
- U + 000 a newline (LF) (
\n
) - A carriage return (U + 000 dCR) (
\r
) - U+2028 line separator
- U+2029 Paragraph separator
There are also characters that can be considered line terminators in real scenarios, such as:
- U+000B Vertical TAB (
\v
) - U + 000 c in page (
\f
) - U + 0085 the next line
As in Java, we can specify flag pattern.dotall to enable. Match all; In C#, regexoptions. Singleline matches all. Therefore, A new flag S has been added to ES9 to complement the above scenario. Let’s take an example
const str = `
hello
world
`;
const r1 = /hello.world/;
console.log(r1.test(str), r1.dotAll, r1.flags);
// false false
const r2 = /hello.world/s; // Add 's' flag
console.log(r2.test(str), r2.dotAll, r2.flags);
// true true s
Copy the code
Some students may have such a question, why is it called S, s stands for singleline does not conflict with m (multiline)? In official words: S stands for singleline, which is also dotAll, with the same meaning. We are embarrassed to add a new mark to do this. S (dotAll) allows. To match any character and does not conflict with m (multiline). You can use 🤪 as you like
RegExp named Capture Groups Specifies the name of the capture group
This feature is a little more fancy, and in old code, if we wanted to match the year, month and day of a date, we might do so
const str = "2021-10-24";
const r1 = /(\d{4})-(\d{2})-(\d{2})/;
const groups = r1.exec(str);
console.log("year:", groups[1]."month:", groups[2]."day:", groups[3]);
// year: 2021 month: 10 day: 24
Copy the code
No problem, old iron 😁, I know several languages are doing so. Until this syntax came out, I didn’t even think about what was wrong with using it directly. The stereotype is that if everyone does it, it’s the right thing to do. But this is in the official words: If I want to get the month in groups, then I need to look at the regular expression carefully. I’ll wrap the month in parentheses and count it. In the second one, following the rules of the regular expression, Groups [2] = 0; groups[2] = 0; groups[2] = 0; Is it cumbersome and easy to be wrong? Me: black question mark. JPG. Ok, so now let’s see how this specification works: do we add one to the previous group? < alias for group >, all together is (? < aliases for groups >… . Let’s modify the above example
const str = "2021-10-24";
const r1 = / (?
\d{4})-(?
\d{2})-(?
\d{2})/
;
const exec = r1.exec(str);
console.log("year:", exec[1]."month:", exec[2]."day:", exec[3]);
console.log("year:", exec.groups.year, "month:", exec.groups.month, "day:", exec.groups.day);
// const { groups: { year, month, day } } = exec;
// console.log("year:", year, "month:", month, "day:", day);
// year: 2021 month: 10 day: 24
// year: 2021 month: 10 day: 24
Copy the code
Is it fancy? No, no, no. That’s not all. We can do fancy matching. If we need to match a complex repeating string, for example we need to match whether a text contains any character aaABbbCCC
const str = 'asjdhkjlhsdkjaaabbbcccahsdkjashdjhsaaaabbbcccasjhdkljdhjkdaaaabbbcccashdlkj';
const r1 = /a{3,}b{3}c{3,}.+a{3,}b{3}c{3,}.+a{3,}b{3}c{3,}/;
const result = r1.test(str);
console.log(result);
// true
Copy the code
Do you feel that the above re a little cumbersome? Here we can use this naming capture to make things a little more fancy. This is also a new feature of naming capture. If we need to match the same result as the previous expression, we can replace it with \k
instead of writing the same expression again
const str = 'asjdhkjlhsdkjaaabbbcccahsdkjashdjhsaaaabbbcccasjhdkljdhjkdaaaabbbcccashdlkj';
const r1 = / (?
a{3,}b{3}c{3,}).+\k
.+\k
/
;
const result = r1.test(str);
console.log(result);
// true
Copy the code
Doesn’t it look much better now? The above example is relatively simple, but in more complex cases, this feature can help us write fewer duplicate rules, and be more accurate, reducing the probability of errors.
This naming capture also applies to string substitution, so let’s use the dates above as an example. If we need to change a date from YYYY-MM-DD to DD /MM/ YYYY, the traditional way to write it is
const str = "2021-10-24";
const r1 = /(\d{4})-(\d{2})-(\d{2})/;
const rd = str.replace(r1,"$3 / $2 / $1");
console.log(rd);
/ / 24/10/2021
Copy the code
Let’s change it to named capture
const str = "2021-10-24";
const r1 = / (?
\d{4})-(?
\d{2})-(?
\d{2})/
;
const rd = str.replace(r1,"$<day>/$<month>/$<year>");
console.log(rd);
/ / 24/10/2021
Copy the code
It’s really faster and more accurate, and less error-prone
Rest/Spread Properties
In ES6 we already have the unwrapping of the remaining elements of an array assignment and the expansion of an array string. In ES9, the remaining properties of object assignment and the expansion of object literals have been added. This is not much to say, let’s give an example
const obj = {
x: 1.y: 2.z: 3.a: "x".b: "y".c: "z"};const{ x, y, z, ... letter } = obj;console.log(x, y, z, letter);
// 1 2 3 { a: 'x', b: 'y', c: 'z' }
Copy the code
This Rest Properties, I don’t know the right word to describe it, but it works pretty well. As in the example above, we solve x, y, z from the object and use… This expansion recombines the remaining a, B, and C into the new object letter, and the original OBj is eaten up and erased, and all is extracted. So Rest Properties is to let X, Y, z come out to receive guests and let the Rest a, B, C go to the letter to Rest. 👾
Let’s move on to expansion, using the example above
// ...
constobjClone = { x, y, z, ... letter, };console.log(objClone);
// { x: 1, y: 2, z: 3, a: 'x', b: 'y', c: 'z' }
Copy the code
We will expand the a, B, and C properties of letter into properties and copy them to objClone. So there’s no bias, there’s no bias, there’s no bias, there’s no bias, there’s no bias, there’s no bias, there’s no bias, there’s no bias, there’s no bias, there’s no bias, there’s no bias, there’s no bias, there’s no bias.
RegExp Lookbehind Assertions Regular expression traceability Assertions
Prior to ES9, EMACScript regex only supported prior assertions, while ES9 officially supported subsequent assertions. I’m not going to expand on this because it doesn’t involve special syntax, so I’ll write a separate article later on regular expressions, which are basically universal in all languages.
Here’s a quick example: Suppose we need to match a string xyz only if it is preceded by uvw
In the absence of a trailing assertion, if we want to match, we usually write like this
const str = "rstuvwxyz123";
const r = /uvw(xyz)/;
const result = r.exec(str);
console.log(result);
// [ 'uvwxyz', 'xyz', index: 3, input: 'rstuvwxyz123', groups: undefined ]
Copy the code
After we have the following assertion
const str = "rstuvwxyz123";
const r = / (? <=uvw)xyz/;
const result = r.exec(str);
console.log(result);
// [ 'xyz', index: 6, input: 'rstuvwxyz123', groups: undefined ]
Copy the code
Some of you must be confused, right? The above kind is not more simple 🤣. I thought it made sense, so I printed out the results. If we’re just checking for a match, which we do in the first way, if we need a match, there’s a difference between the two. If a trailing assertion is used, uVW does not appear in the match result.
Above we are only talking about the positive assertion of the subsequent assertion, and the opposite negative assertion (reverse negation lookup). Let’s continue with the example above, but this time we need to match xyz before uVW
const str = "rstuvwxyz123";
const r = / (?
;
const result = r.exec(str);
console.log(result);
// null
Copy the code
Now xyz is right next to Uvw, so nothing matches, so let’s change it and insert a 0 in between uvw
const str = "rstuv0wxyz123";
const r = / (?
;
const result = r.exec(str);
console.log(result);
// [ 'xyz', index: 7, input: 'rstuv0wxyz123', groups: undefined ]
Copy the code
And now we have a match
RegExp Unicode Property Escapes Unicode properties in regular expressions
When we use regular expressions, we often need to match the special symbols of different languages. For example, we have common requirements: A form cannot enter emoji or other special characters, a DiuDiuDe other languages such as, in accordance with the previous writing, we often have to introduce three party libraries to match these strange characters, but the scope of these strange characters has been constantly changing, so we introduce these libraries exist the problem need to be updated frequently. Emoji, for example, are update freaks. To match emoji, you have to constantly update the emoji matching library to properly filter them out. Now that’s all we need
const str = "hi, 🤣";
const r1 = /\p{Emoji}/gu;
console.log(r1.exec(str));
// ['🤣', index: 4, input: 'hi, 🤣', groups: undefined]
Copy the code
You can easily match emojis. Some students may directly black question mark.jpg, right, you read it correctly, just need to do so you can match the emoji, there are also a number of Unicode emoji things, for those who want to know more, See details and more curly bracketed emojis here, like the more commonly used Emoji_Presentation?
What if I want to match something that’s not Emoji? We just replace small p with big P, isn’t that easy?
const str = "hi, 🤣";
const r2 = /(\P{Emoji})*/gu;
console.log(r2.exec(str));
/ / [' hi, ', ', the index: 0, input: 'hi, 🤣' groups: undefined]
Copy the code
This feature is very powerful, but it is recommended not to use it in a hurry. After all, it depends on the browser. For those of you who want to learn more about this feature, go to mDN-Unicode Property Escapes, and I won’t go into that, but we’ll just
Promise.prototype.finally
This method is executed after a Promise then or catch has been executed. It is usually used to modify the loading state or to close a resource when it is used up, such as:
this.loading = true;
xxxApi
.listUser()
.then((resp) = > {
// do something...
})
.catch((e) = > {
// do something...
})
.finally(() = > {
this.loading = false;
});
Copy the code
Not only the two scenarios mentioned above, but if we need to do something at the end of the task, we can use this method.
Asynchronous Iteration An Asynchronous iterator
In ES9, the use of for-await-of was added. For synchronous iterators, let’s say we have an array of promises and wait for the Promise elements to run out one by one, as we did before
function newPromise(delay) {
return new Promise((resolve) = > {
setTimeout(() = > {
console.log(`resolve:`, delay);
resolve(delay);
}, delay);
});
}
async function test() {
const arr = [newPromise(3000), newPromise(2000), newPromise(4000)];
const before = Date.now();
for (const item of arr) {
console.log(Date.now(), await newPromise(1234));
console.log(Date.now(), await item);
}
console.log(Date.now() - before);
}
test();
// resolve: 1234
/ / 1635088618117 1234
// resolve: 2000
// resolve: 3000
/ / 1635088619374 3000
// resolve: 4000
// resolve: 1234
/ / 1635088621117 1234
/ / 1635088622369 2000
// resolve: 1234
/ / 1635088622369 1234
/ / 1635088623616 4000
/ / 5500
Copy the code
If we switch to for-await-of
async function test2() {
const arr = [newPromise(3000), newPromise(2000), newPromise(4000)];
const before = Date.now();
for await (const item of arr) {
console.log(Date.now(), await newPromise(1234));
console.log(Date.now(), item);
}
console.log(Date.now() - before);
}
test2();
// resolve: 2000
// resolve: 3000
// resolve: 4000
// resolve: 1234
/ / 1635088545345 1234
/ / 1635088546584 3000
// resolve: 1234
/ / 1635088546584 1234
/ / 1635088547826 2000
// resolve: 1234
/ / 1635088547826 1234
/ / 1635088549066 4000
/ / 6733
Copy the code
If you just look at the results, it’s pretty much the same, the end result is the same. But the actual implementation is different:
- The way to await directly in the loop is to wait for each independent Promise. We can see from the print of the first Promise that all tasks will be resolved in turn according to the length of delay
- Using for-await-of uninterrupts the array before executing the code inside the loop.
In the final analysis, the timing of pushing events into the event cycle is different, and the use of actual business scenarios depends on the needs of students. Different writing methods are still different, so don’t make a mistake.
In ES9, we added a Symbol. AsyncIterator to define the default asynchronous iterator for objects.
Let’s take an example to create an asynchronous iterable
const ai = {
// The whole method is used to terminate
dispose() {
this.disposed = true; },Symbol.asyncIterator]() {
return {
// All the following methods use the arrow function to avoid writing that by hand
next: () = > {
return new Promise((resolve, _) = > {
setTimeout(() = > {
resolve({
done:!!!!!this.disposed,
value: Date.now(),
});
}, 200); }); }}; }};async function test() {
for await (const it of ai) {
console.log(it);
}
}
test();
setTimeout(() = > {
ai.dispose();
}, 1000); // End in one second
/ / 1635170851461
/ / 1635170851679
/ / 1635170851881
/ / 1635170852084
Copy the code
In the above example, we declare a symbol. asyncIterator property method on an object that returns an object containing a next method. Look familiar? We declare a synchronous iterator and do the same. If we use a synchronous iterator, we simply return {value, done}. Value is the actual value, and done is a Boolean that indicates whether the iterator is finished iterating. In an asynchronous iterator, we return a Promise object and Promise resolve({value, done}). Value and done have the same meanings as the synchronization iterators described above.
And some of you might be wondering if this is too much trouble to write, but we can do this in a synchronous iterator
const iterator = {
[Symbol.iterator]: function* () {
yield `a`;
yield `b`;
yield `c`; }};for (const it of iterator) {
console.log(it);
}
// a
// b
// c
Copy the code
Is it possible to write this in an asynchronous iterator? Function async is an asynchronous iterator. Function async is an asynchronous iterator
const asyncIterator = {
[Symbol.asyncIterator]: async function* () {
yield `a`;
yield `b`;
yield `c`; }}; (async() = > {for await (const x of asyncIterator) {
console.log(x);
}
})();
// a
// b
// c
Copy the code
Does it look like OJBK? The same
function* it() {
yield `a`;
yield `b`;
yield `c`;
}
for (const i of it()) {
console.log(i);
}
// a
// b
// c
async function* ait() {
yield `a`;
yield `b`;
yield `c`;
}
(async() = > {for await (const i of ait()) {
console.log(i);
}
})();
// a
// b
// c
Copy the code
Does this look like it’s in N languages? It looks like a standard Stream implementation. Everyone is pretty much the same, so it’s pretty easy to understand
conclusion
The article here to say goodbye to everyone, a few features sometimes really anxious to write tens of thousands of words, because there are too many things involved, not a simple few words can say clearly. However, this time it does not allow ah, I can only say good night with tears in my eyes.
This article also wrote me a few days, write this, write there, finally is finished. I feel that this ES9 is all regular-related 🤣. I left an assignment for myself in the article, and I have time to write an article about the use of regulars. This regular I believe that many students are confused, I have seen many N years of experience on the regular is not very understanding. So this re, I certainly want water, please look forward to it, do not say package meeting, at least can blow cowhast with others.
If the article is helpful to you, welcome to like, comment, pay attention to, collect, share, your support is my code word power, thank you!! 🌈
If the content of the article is wrong, welcome to correct, exchange, thank 😘
Finally, you can click here to join the QQ group FlutterCandies🍭 and communicate with various gurus
The resources
- Introduction to EMACScript and the latest specification
- The completed ES proposal
- Quick introduction to one of ES features (ES7-ES8)
- EventLoop
- MDN – Unicode property escapes
- UNICODE emoji