ECMAScript13 中11個令人驚歎的 JavaScript 新特性

前言

與許多其他編程語言一樣,JavaScript 也在不斷髮展。每年,該語言都會通過新功能變得更加強大,使開發人員能夠編寫更具表現力和簡潔的代碼。 小編今天就爲大家介紹ES13中添加的最新功能,並查看其用法示例以更好地理解它們。

1.類

在ES13之前,類字段只能在構造函數中聲明。與許多其他語言不同,無法在類的最外層作用域中聲明或定義它們。

class Car {
    constructor() {
      this.color = 'blue';
      this.age = 2;
    }
  }
  const car = new Car();
  console.log(car.color); // blue
  console.log(car.age); // 

而ES13 消除了這個限制。現在我們可以編寫這樣的代碼:

class Car {
  color = 'blue';
  age = 2;
}const car = new Car();
console.log(car.color); // blue
console.log(car.age); // 2

2.私有方法和字段

ES13以前,不可能在類中聲明私有成員。成員傳統上帶有下劃線 ( _) 前綴,以表明它是私有的,但仍然可以從類外部訪問和修改它。

class Person {
  _firstName = 'Joseph';
  _lastName = 'Stevens';  get name() {
    return `${this._firstName} ${this._lastName}`;
  }
}const person = new Person();
console.log(person.name); // Joseph Stevens
// 仍可以從類外部訪問 // 原本打算設爲私有的成員
console.log(person._firstName); // Joseph
console.log(person._lastName); // Stevens
// 也可以修改
person._firstName = 'Robert';
person._lastName = 'Becker';console.log(person.name); // Robert Becker

使用 ES13,我們現在可以通過在類前面添加 ( #) 來向類添加私有字段和成員。嘗試從外部訪問這些類將會引發錯誤:

class Person {
  #firstName = 'Joseph';
  #lastName = 'Stevens';  get name() {
    return `${this.#firstName} ${this.#lastName}`;
  }
}const person = new Person();
console.log(person.name);
// 語法錯誤:私有字段 '#firstName' 必須在一個外層類中聲明
console.log(person.#firstName);
console.log(person.#lastName);

3.await頂層操作

在 JavaScript 中,await運算符用於暫停執行,直到 一個Promise被解決(執行或拒絕)。 以前只能在async中使用此運算符。不可以在全局作用域中直接使用await。

function setTimeoutAsync(timeout) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, timeout);
  });
}
//語法錯誤:await 僅在異步函數中有效
await setTimeoutAsync(3000);

有了 ES13,現在我們可以:

function setTimeoutAsync(timeout) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, timeout);
  });
}
//  等待超時 - 沒有錯誤拋出
await setTimeoutAsync(3000);

4.靜態類字段和靜態私有方法

現在可以在 ES13 中爲類聲明靜態字段和靜態私有方法。靜態方法可以使用關鍵字this訪問類中的其他私有/公共靜態成員,實例方法可以使用this.constructor訪問他們。

class Person {
  static #count = 0;  static getCount() {
    return this.#count;
  }  constructor() {
    this.constructor.#incrementCount();
  }  static #incrementCount() {
    this.#count++;
  }
}const person1 = new Person();
const person2 = new Person();console.log(Person.getCount()); // 2

5.類靜態塊

ES13 引入了一項特性,允許開發者定義僅在創建類時執行一次的靜態塊。這一特性與其他面向對象編程語言(如 C# 和 Java)中的靜態構造函數相似。

在一個類的主體中,你可以定義任意數量的靜態 {} 初始化塊。它們會按照聲明的順序與任何交錯的靜態字段初始值設定項一起執行。此外,你還可以通過塊中的 super 關鍵字訪問超類的靜態屬性。這爲開發者提供了更多的靈活性和控制能力。

class Vehicle {
  static defaultColor = 'blue';
}class Car extends Vehicle {
  static colors = [];  static {
    this.colors.push(super.defaultColor, 'red');
  }  static {
    this.colors.push('green');
  }
}console.log(Car.colors); // [ 'blue', 'red', 'green' ]

6.檢查對象中的私有字段

開發者如今可以利用這一新功能,使用運算符in來方便地檢查對象是否包含某個特定的私有字段。

class Car {
  #color;  hasColor() {
    return #color in this;
  }
}const car = new Car();
console.log(car.hasColor()); // true;

通過運算符in,可以準確區分不同類中具有相同名稱的私有字段。

class Car {
  #color;  hasColor() {
    return #color in this;
  }
}class House {
  #color;  hasColor() {
    return #color in this;
  }
}const car = new Car();
const house = new House();console.log(car.hasColor()); // true;
console.log(car.hasColor.call(house)); // false
console.log(house.hasColor()); // true
console.log(house.hasColor.call(car)); // false

7.at() 索引方法

在 JavaScript 中,我們通常使用方括號[]來訪問數組的第 t 個元素。這個過程非常簡單,但實際上我們只是訪問了索引爲 t-1 的數組屬性而已。

const arr = ['a', 'b', 'c', 'd'];
console.log(arr[1]); // b

然而,當我們希望通過方括號來訪問數組末尾的第 N 個元素時,我們需要使用索引 arr.length - N。

const arr = ['a', 'b', 'c', 'd'];
// 從末尾開始第一個元素
console.log(arr[arr.length - 1]); // d
// 倒數第二個元素 console.log 
console.log(arr[arr.length - 2]); // c

藉助全新的at()方法,可以以更加精簡和富有表現力的方式來實現這一目標。要訪問數組末尾的第N個元素,只需將負值-N作爲參數傳遞給at()方法即可。

const arr = ['a', 'b', 'c', 'd'];
// 從末尾開始第一個元素
console.log(arr.at(-1)); // d
// 倒數第二個元素 console.log 
console.log(arr.at(-2)); // c

除了數組之外,字符串和TypedArray對象現在也有at()方法。

const str = 'Coding Beauty';
console.log(str.at(-1)); // y
console.log(str.at(-2)); // tconst typedArray = new Uint8Array([16, 32, 48, 64]);
console.log(typedArray.at(-1)); // 64
console.log(typedArray.at(-2)); // 48

8.正則表達式匹配索引

在ES13之前,我們只能獲取字符串中正則表達式匹配的起始索引,

const str = 'sun and moon';const regex = /and/;const matchObj = regex.exec(str);// [ 'and', index: 4, input: 'sun and moon', groups: undefined ]
console.log(matchObj);

使用ES13之後,可以通過指定一個/d正則表達式標誌來獲取匹配開始和結束的兩個索引。這一特性賦予了更多的靈活性和控制能力。

const str = 'sun and moon';
const regex = /and/d;
const matchObj = regex.exec(str);
/**
[
  'and',
  index: 4,
  input: 'sun and moon',
  groups: undefined,
  indices: [ [ 4, 7 ], groups: undefined ]
]
 */
console.log(matchObj);

設置標誌後d,返回的對象將具有indices包含起始索引和結束索引的屬性。

9.Object.hasOwn()方法

在 JavaScript 中,我們可以使用Object.prototype.hasOwnProperty()方法來檢查對象是否具有給定的屬性。

class Car {
  color = 'green';
  age = 2;
}const car = new Car();console.log(car.hasOwnProperty('age')); // true
console.log(car.hasOwnProperty('name')); // false

然而,這種方法存在一些問題。首先,Object.prototype.hasOwnProperty()方法並未受到保護,這意味着我們可以通過自定義的hasOwnProperty()方法來覆蓋它,而這個自定義方法可能會具有與Object.prototype.hasOwnProperty()不同的行爲。需要額外注意的是這一點。

class Car {
  color = 'green';
  age = 2;  // This method does not tell us whether an object of
  // this class has a given property.
  hasOwnProperty() {
    return false;
  }
}const car = new Car();console.log(car.hasOwnProperty('age')); // false
console.log(car.hasOwnProperty('name')); // false

另外一個問題是,如果我們使用了 null 原型(通過 Object.create(null) 創建的對象),那麼試圖調用該方法將會產生錯誤。

const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
// TypeError: obj.hasOwnProperty 不是函數
console.log(obj.hasOwnProperty('color'));

爲了克服這些問題,我們可以利用屬性調用方法Object.prototype.hasOwnProperty.call()來解決。具體示例如下所示:

const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;console.log(Object.prototype.hasOwnProperty.call(obj, 'color')); // true
console.log(Object.prototype.hasOwnProperty.call(obj, 'name')); // false

這種方式並不十分便利。爲了避免重複,我們可以編寫一個可重用的函數,這樣可以使我們的代碼更加簡潔和高效:

function objHasOwnProp(obj, propertyKey) {
  return Object.prototype.hasOwnProperty.call(obj, propertyKey);
}const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;console.log(objHasOwnProp(obj, 'color')); // true
console.log(objHasOwnProp(obj, 'name')); // false

現在不需要在那樣做了,我們還可以使用全新的內置方法Object.hasOwn()來處理這個問題。它與我們之前編寫的可重用函數類似,接受對象和屬性作爲參數,並且返回一個布爾值,如果指定的屬性是對象的直接屬性,則返回true;否則返回false。

const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;console.log(Object.hasOwn(obj, 'color')); // true
console.log(Object.hasOwn(obj, 'name')); // false

10.錯誤原因屬性

現在,錯誤對象已經增加了一個cause屬性,該屬性用於指定導致錯誤拋出的原始錯誤。通過這種方式,我們可以爲錯誤添加額外的上下文信息,從而更好地診斷意外的行爲。要指定錯誤的原因,我們可以在作爲構造函數的第二個參數傳遞給Error()的對象中設置屬性來實現。這種方法能夠提供更豐富的錯誤追蹤和調試信息。

function userAction() {
  try {
    apiCallThatCanThrow();
  } catch (err) {
    throw new Error('New error message', { cause: err });
  }
}try {
  userAction();
} catch (err) {
  console.log(err);
  console.log(`Cause by: ${err.cause}`);
}

11.從數組最後查找

在 JavaScript 中,我們已經可以使用Array的find()方法來查找數組中滿足指定測試條件的元素。類似地,我們也可以使用findIndex()方法來獲取滿足條件的元素的索引值。儘管find()和findIndex()都是從數組的第一個元素開始搜索,但在某些情況下,從最後一個元素開始搜索可能會更有效。

有些情況下,我們知道從數組的末尾進行查找可能會獲得更好的性能表現。例如,在這裏我們嘗試查找數組中prop屬性等於"value"的項目。這時候,可以通過使用reverse()方法將數組反轉,然後使用find()和findIndex()方法來從末尾開始搜索。下面是具體的實現示例:

const letters = [
  { value: 'v' },
  { value: 'w' },
  { value: 'x' },
  { value: 'y' },
  { value: 'z' },
];const found = letters.find((item) => item.value === 'y');
const foundIndex = letters.findIndex((item) => item.value === 'y');console.log(found); // { value: 'y' }
console.log(foundIndex); // 3

上面的代碼可以獲取正確結果,但由於目標對象更接近數組的尾部,如果我們使用findLast()和findLastIndex()方法來從數組的末尾進行搜索,很可能能夠顯著提升程序的執行效率。通過這種方式,我們可以更快地找到所需的元素或索引,從而優化代碼性能。

const letters = [
  { value: 'v' },
  { value: 'w' },
  { value: 'x' },
  { value: 'y' },
  { value: 'z' },
];const found = letters.findLast((item) => item.value === 'y');
const foundIndex = letters.findLastIndex((item) => item.value === 'y');console.log(found); // { value: 'y' }
console.log(foundIndex); // 3

在一些特定的使用場景中,我們可能需要從數組的末尾開始搜索來獲取準確的元素。舉個例子,假設我們要查找數字列表中的最後一個偶數,使用find()或findIndex()方法可能會導致錯誤的結果:

const nums = [7, 14, 3, 8, 10, 9];
// 給出 14,而不是 10
const lastEven = nums.find((value) => value % 2 === 0);
// 給出 1,而不是 4 
const lastEvenIndex = nums.findIndex((value) => value % 2 === 0);console.log(lastEven); // 14
console.log(lastEvenIndex); // 1

如果我們在調用reverse()方法之前使用數組的slice()方法創建新的數組副本,就可以避免不必要地改變原始數組的順序。然而,在處理大型數組時,這種方法可能會導致性能問題,因爲需要複製整個數組。

此外,findIndex()方法在反轉數組時仍然無法達到預期效果,因爲元素的反轉會導致它們在原始數組中的索引改變。爲了獲取元素的原始索引,我們需要進行額外的計算,這意味着需要編寫更多的代碼來處理這種情況。

const nums = [7, 14, 3, 8, 10, 9];
// 在調用reverse()之前使用展開語法複製整個數組
// calling reverse()
const reversed = [...nums].reverse();
// 正確給出 10 
const lastEven = reversed.find((value) => value % 2 === 0);
// 給出 1,而不是 4 
const reversedIndex = reversed.findIndex((value) => value % 2 === 0);
// 需要重新計算得到原始索引
const lastEvenIndex = reversed.length - 1 - reversedIndex;console.log(lastEven); // 10
console.log(reversedIndex); // 1
console.log(lastEvenIndex); // 4

使用findLast()和findLastIndex()方法在需要查找數組中最後一個符合條件的元素或索引時非常實用。它們能夠準確地定位目標對象,並且從數組末尾開始搜索,提供了高效的解決方案。

const nums = [7, 14, 3, 8, 10, 9];const lastEven = nums.findLast((num) => num % 2 === 0);
const lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0);console.log(lastEven); // 10
console.log(lastEvenIndex); // 4

結論

ES13 爲 JavaScript 帶來了一系列令人振奮的新功能,我們已經有幸見識了它們的魅力。通過運用這些功能,開發人員的工作效率將得到極大提升,同時也能以更加簡潔、明晰的方式書寫出更加純淨、精煉的代碼。這些新特性爲我們帶來了更大的靈活性和便利性,使得我們的開發過程更加高效、愉悅。

原文鏈接:https://medium.com/coding-beauty/es13-javascript-features-eed7ed2f1497

擴展鏈接:

高級SQL分析函數-如何用窗口函數進行排名計算

3D模型+BI分析,打造全新的交互式3D可視化大屏開發方案

React + Springboot + Quartz,從0實現Excel報表自動化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章