您好,登錄后才能下訂單哦!
本篇內容介紹了“ECMAScript新特性有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
在ES2017中,引入了 async
函數和 await
關鍵字,以簡化 Promise
的使用,但是 await
關鍵字只能在 async
函數內部使用。嘗試在異步函數之外使用 await
就會報錯:SyntaxError - SyntaxError: await is only valid in async function
。
頂層 await
允許我們在 async
函數外面使用 await
關鍵字。它允許模塊充當大型異步函數,通過頂層 await
,這些 ECMAScript
模塊可以等待資源加載。這樣其他導入這些模塊的模塊在執行代碼之前要等待資源加載完再去執行。
由于 await
僅在 async
函數中可用,因此模塊可以通過將代碼包裝在 async
函數中來在代碼中包含 await
:
// a.js import fetch from "node-fetch"; let users; export const fetchUsers = async () => { const resp = await fetch('https://jsonplaceholder.typicode.com/users'); users = resp.json(); } fetchUsers(); export { users }; // usingAwait.js import {users} from './a.js'; console.log('users: ', users); console.log('usingAwait module');
我們還可以立即調用頂層async
函數(IIAFE):
import fetch from "node-fetch"; (async () => { const resp = await fetch('https://jsonplaceholder.typicode.com/users'); users = resp.json(); })(); export { users };
這樣會有一個缺點,直接導入的 users
是 undefined
,需要在異步執行完成之后才能訪問它:
// usingAwait.js import {users} from './a.js'; console.log('users:', users); // undefined setTimeout(() => { console.log('users:', users); }, 100); console.log('usingAwait module');
當然,這種方法并不安全,因為如果異步函數執行花費的時間超過100毫秒, 它就不會起作用了,users
仍然是 undefined
。
另一個方法是導出一個 promise
,讓導入模塊知道數據已經準備好了:
//a.js import fetch from "node-fetch"; export default (async () => { const resp = await fetch('https://jsonplaceholder.typicode.com/users'); users = resp.json(); })(); export { users }; //usingAwait.js import promise, {users} from './a.js'; promise.then(() => { console.log('usingAwait module'); setTimeout(() => console.log('users:', users), 100); });
雖然這種方法似乎是給出了預期的結果,但是有一定的局限性:導入模塊必須了解這種模式才能正確使用它。
而頂層await
就可以解決這些問題:
// a.js const resp = await fetch('https://jsonplaceholder.typicode.com/users'); const users = resp.json(); export { users}; // usingAwait.js import {users} from './a.mjs'; console.log(users); console.log('usingAwait module');
頂級 await
在以下場景中將非常有用:
動態加載模塊:
const strings = await import(`/i18n/${navigator.language}`);
資源初始化:
const connection = await dbConnector();
依賴回退:
let translations; try { translations = await import('https://app.fr.json'); } catch { translations = await import('https://fallback.en.json'); }
該特性的瀏覽器支持如下:
在ES2022之前,可以使用 Object.prototype.hasOwnProperty()
來檢查一個屬性是否屬于對象。
Object.hasOwn
特性是一種更簡潔、更可靠的檢查屬性是否直接設置在對象上的方法:
const example = { property: '123' }; console.log(Object.prototype.hasOwnProperty.call(example, 'property')); console.log(Object.hasOwn(example, 'property'));
該特性的瀏覽器支持如下:
at()
是一個數組方法,用于通過給定索引來獲取數組元素。當給定索引為正時,這種新方法與使用括號表示法訪問具有相同的行為。當給出負整數索引時,就會從數組的最后一項開始檢索:
const array = [0,1,2,3,4,5]; console.log(array[array.length-1]); // 5 console.log(array.at(-1)); // 5 console.log(array[array.lenght-2]); // 4 console.log(array.at(-2)); // 4
除了數組,字符串也可以使用at()
方法進行索引:
const str = "hello world"; console.log(str[str.length - 1]); // d console.log(str.at(-1)); // d
在 ECMAScript 2022 規范中,new Error()
中可以指定導致它的原因:
function readFiles(filePaths) { return filePaths.map( (filePath) => { try { // ··· } catch (error) { throw new Error( `While processing ${filePath}`, {cause: error} ); } }); }
該特性允許我們利用 d
字符來表示我們想要匹配字符串的開始和結束索引。以前,只能在字符串匹配操作期間獲得一個包含提取的字符串和索引信息的數組。在某些情況下,這是不夠的。因此,在這個規范中,如果設置標志 /d
,將額外獲得一個帶有開始和結束索引的數組。
const matchObj = /(a+)(b+)/d.exec('aaaabb'); console.log(matchObj[1]) // 'aaaa' console.log(matchObj[2]) // 'bb'
由于 /d
標識的存在,matchObj
還有一個屬性.indices
,它用來記錄捕獲的每個編號組:
console.log(matchObj.indices[1]) // [0, 4] console.log(matchObj.indices[2]) // [4, 6]
我們還可以使用命名組:
const matchObj = /(?<as>a+)(?<bs>b+)/d.exec('aaaabb'); console.log(matchObj.groups.as); // 'aaaa' console.log(matchObj.groups.bs); // 'bb'
這里給兩個字符匹配分別命名為as
和bs
,然后就可以通過groups
來獲取到這兩個命名分別匹配到的字符串。
它們的索引存儲在 matchObj.indices.groups
中:
console.log(matchObj.indices.groups.as); // [0, 4] console.log(matchObj.indices.groups.bs); // [4, 6]
匹配索引的一個重要用途就是指向語法錯誤所在位置的解析器。下面的代碼解決了一個相關問題:它指向引用內容的開始和結束位置。
const reQuoted = /“([^”]+)”/dgu; function pointToQuotedText(str) { const startIndices = new Set(); const endIndices = new Set(); for (const match of str.matchAll(reQuoted)) { const [start, end] = match.indices[1]; startIndices.add(start); endIndices.add(end); } let result = ''; for (let index=0; index < str.length; index++) { if (startIndices.has(index)) { result += '['; } else if (endIndices.has(index+1)) { result += ']'; } else { result += ' '; } } return result; } console.log(pointToQuotedText('They said “hello” and “goodbye”.')); // ' [ ] [ ] '
公共類字段允許我們使用賦值運算符 (=) 將實例屬性添加到類定義中。下面是一個計數器的例子:
import React, { Component } from "react"; export class Incrementor extends Component { constructor() { super(); this.state = { count: 0, }; this.increment = this.increment.bind(this); } increment() { this.setState({ count: this.state.count + 1 }); } render() { return ( <button onClick={this.increment}>Increment: {this.state.count}</button> ); } }
在這個例子中,在構造函數中定義了實例字段和綁定方法,通過新的類語法,可以使代碼更加直觀。新的公共類字段語法允許我們直接將實例屬性作為屬性添加到類上,而無需使用構造函數方法。這樣就簡化了類的定義,使代碼更加簡潔、可讀:
import React from "react"; export class Incrementor extends React.Component { state = { count: 0 }; increment = () => this.setState({ count: this.state.count + 1 }); render = () => ( <button onClick={this.increment}>Increment: {this.state.count}</button> ); }
有些小伙伴可能就疑問了,這個功能很早就可以使用了呀。但是它現在還不是標準的 ECMAScript,默認是不開啟的,如果使用 create-react-app
創建 React 項目,那么它默認是啟用的,否則我們必須使用正確的babel插件才能正常使用(@babel/preset-env
)。
下面來看看關于公共實例字段的注意事項:
公共實例字段存在于每個創建的類實例上。它們要么是在Object.defineProperty()
中添加,要么是在基類中的構造時添加(構造函數主體執行之前執行),要么在子類的super()
返回之后添加:
class Incrementor { count = 0 } const instance = new Incrementor(); console.log(instance.count); // 0
未初始化的字段會自動設置為 undefined
:
class Incrementor { count } const instance = new Incrementor(); console.assert(instance.hasOwnProperty('count')); console.log(instance.count); // undefined
可以進行字段的計算:
const PREFIX = 'main'; class Incrementor { [`${PREFIX}Count`] = 0 } const instance = new Incrementor(); console.log(instance.mainCount); // 0
默認情況下,ES6 中所有屬性都是公共的,可以在類外檢查或修改。下面來看一個例子:
class TimeTracker { name = 'zhangsan'; project = 'blog'; hours = 0; set addHours(hour) { this.hours += hour; } get timeSheet() { return `${this.name} works ${this.hours || 'nothing'} hours on ${this.project}`; } } let person = new TimeTracker(); person.addHours = 2; // 標準 setter person.hours = 4; // 繞過 setter 進行設置 person.timeSheet;
可以看到,在類中沒有任何措施可以防止在不調用 setter
的情況下更改屬性。
而私有類字段將使用哈希#
前綴定義,從上面的示例中,可以修改它以包含私有類字段,以防止在類方法之外更改屬性:
class TimeTracker { name = 'zhangsan'; project = 'blog'; #hours = 0; // 私有類字段 set addHours(hour) { this.#hours += hour; } get timeSheet() { return `${this.name} works ${this.#hours || 'nothing'} hours on ${this.project}`; } } let person = new TimeTracker(); person.addHours = 4; // 標準 setter person.timeSheet // zhangsan works 4 hours on blog
當嘗試在 setter
方法之外修改私有類字段時,就會報錯:
person.hours = 4 // Error Private field '#hours' must be declared in an enclosing class
還可以將方法或 getter/setter
設為私有,只需要給這些方法名稱前面加#
即可:
class TimeTracker { name = 'zhangsan'; project = 'blog'; #hours = 0; // 私有類字段 set #addHours(hour) { this.#hours += hour; } get #timeSheet() { return `${this.name} works ${this.#hours || 'nothing'} hours on ${this.project}`; } constructor(hours) { this.#addHours = hours; console.log(this.#timeSheet); } } let person = new TimeTracker(4); // zhangsan works 4 hours on blog
由于嘗試訪問對象上不存在的私有字段會發生異常,因此需要能夠檢查對象是否具有給定的私有字段。可以使用 in
運算符來檢查對象上是否有私有字段:
class Example { #field static isExampleInstance(object) { return #field in object; } }
在ES6中,不能在類的每個實例中訪問靜態字段或方法,只能在原型中訪問。ES 2022 提供了一種在 JavaScript 中使用 static
關鍵字聲明靜態類字段的方法。下面來看一個例子:
class Shape { static color = 'blue'; static getColor() { return this.color; } getMessage() { return `color:${this.color}` ; } }
可以從類本身訪問靜態字段和方法:
console.log(Shape.color); // blue console.log(Shape.getColor()); // blue console.log('color' in Shape); // true console.log('getColor' in Shape); // true console.log('getMessage' in Shape); // false
實例不能訪問靜態字段和方法:
const shapeInstance = new Shape(); console.log(shapeInstance.color); // undefined console.log(shapeInstance.getColor); // undefined console.log(shapeInstance.getMessage());// color:undefined
靜態字段只能通過靜態方法訪問:
console.log(Shape.getColor()); // blue console.log(Shape.getMessage()); //TypeError: Shape.getMessage is not a function
這里的 Shape.getMessage()
就報錯了,因為 getMessage
不是一個靜態函數,所以它不能通過類名 Shape
訪問。可以通過以下方式來解決這個問題:
getMessage() { return `color:${Shape.color}` ; }
靜態字段和方法是從父類繼承的:
class Rectangle extends Shape { } console.log(Rectangle.color); // blue console.log(Rectangle.getColor()); // blue console.log('color' in Rectangle); // true console.log('getColor' in Rectangle); // true console.log('getMessage' in Rectangle); // false
與私有實例字段和方法一樣,靜態私有字段和方法也使用哈希 (#) 前綴來定義:
class Shape { static #color = 'blue'; static #getColor() { return this.#color; } getMessage() { return `color:${Shape.#getColor()}` ; } } const shapeInstance = new Shape(); shapeInstance.getMessage(); // color:blue
私有靜態字段有一個限制:只有定義私有靜態字段的類才能訪問該字段。這可能在使用 this
時導致出乎意料的情況:
class Shape { static #color = 'blue'; static #getColor() { return this.#color; } static getMessage() { return `color:${this.#color}` ; } getMessageNonStatic() { return `color:${this.#getColor()}` ; } } class Rectangle extends Shape {} console.log(Rectangle.getMessage()); // Uncaught TypeError: Cannot read private member #color from an object whose class did not declare it const rectangle = new Rectangle(); console.log(rectangle.getMessageNonStatic()); // TypeError: Cannot read private member #getColor from an object whose class did not declare it
在這個例子中,this
指向的是 Rectangle
類,它無權訪問私有字段 #color
。當我們嘗試調用 Rectangle.getMessage()
時,它無法讀取 #color
并拋出了 TypeError
。可以這樣來進行修改:
class Shape { static #color = 'blue'; static #getColor() { return this.#color; } static getMessage() { return `${Shape.#color}`; } getMessageNonStatic() { return `color:${Shape.#getColor()} color`; } } class Rectangle extends Shape {} console.log(Rectangle.getMessage()); // color:blue const rectangle = new Rectangle(); console.log(rectangle.getMessageNonStatic()); // color:blue
靜態私有和公共字段只能讓我們在類定義期間執行靜態成員的每個字段初始化。如果我們需要在初始化期間像 try…catch
一樣進行異常處理,就不得不在類之外編寫此邏輯。該規范就提供了一種在類聲明/定義期間評估靜態初始化代碼塊的優雅方法,可以訪問類的私有字段。
先來看一個例子:
class Person { static GENDER = "Male" static TOTAL_EMPLOYED; static TOTAL_UNEMPLOYED; try { // ... } catch { // ... } }
上面的代碼就會引發錯誤,可以使用類靜態塊來重構它,只需將try...catch
包裹在 static
中即可:
class Person { static GENDER = "Male" static TOTAL_EMPLOYED; static TOTAL_UNEMPLOYED; static { try { // ... } catch { // ... } } }
此外,類靜態塊提供對詞法范圍的私有字段和方法的特權訪問。這里需要在具有實例私有字段的類和同一范圍內的函數之間共享信息的情況下很有用。
let getData; class Person { #x constructor(x) { this.#x = { data: x }; } static { getData = (obj) => obj.#x; } } function readPrivateData(obj) { return getData(obj).data; } const john = new Person([2,4,6,8]); readPrivateData(john); // [2,4,6,8]
這里,Person
類與 readPrivateData
函數共享了私有實例屬性。
“ECMAScript新特性有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。