自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一口氣看完,ES8,9,10,13,14,15中30多個最具變革性的JavaScript特性

開發(fā) 前端
總的來說,ES15對JavaScript來說是一個重大飛躍,包含了幾個對現(xiàn)代開發(fā)至關重要的特性。幫助你以更簡潔、更富表現(xiàn)力、更清晰的方式編寫更干凈的代碼。

ES8包含了許多有價值的特性,徹底改變了我們編寫JavaScript的方式。

代碼變得更簡潔、更易編寫,并升級了新功能。

我們來看看這些特性,看看你是否錯過了哪些。

1.尾隨逗號

在ES8之前,尾隨逗號會導致語法錯誤!

? 之前:

const colors = [
    'red',
    'blue',
    'green',
    'yellow', // ? 不允許
];

const person = {
    name: 'Tari Ibaba',
    site: 'codingbeautydev.com' // ? 不行
};

但這引發(fā)了一些問題,重新排列列表會帶來麻煩:

圖片圖片

我們還必須總是在最后一項添加逗號才能添加新項 — 這會使git差異變得混亂:

圖片圖片

所以ES8修復了所有這些:

? 現(xiàn)在:

const colors = [
    'red',
    'blue',
    'green',
    'yellow', // ? yes
];

const person = {
    name: 'Tari Ibaba',
    site: 'codingbeautydev.com', // ? yes
};

它們帶來的好處也使得像Prettier這樣的工具在格式化后默認添加它們:

圖片圖片

2.async/await

這就是async/await的起源!

不再需要煩人的then()嵌套:

? 之前:

wait().then(() => {
    console.log('WHERE ARE YOU?! ??');
});

function wait() {
    return new Promise((resolve) =>
        setTimeout(resolve, 10 * 1000)
    );
}

? 現(xiàn)在:

// ?? immediately invoked function expression (IIFE)
(async () => {
    await wait();
    console.log('WHERE ARE YOU?! ??');
})();

function wait() {
    return new Promise((resolve) =>
        setTimeout(resolve, 10 * 1000)
    );
}

區(qū)別很明顯:

? 之前:

function getSuggestion() {
    fetch('https://api.example/suggestion', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({}) // Pass the necessary payload here
    })
    .then((res) => {
        return res.json();
    })
    .then((data) => {
        const { suggestion } = data;
        console.log(suggestion);
    });
}

? 現(xiàn)在:

async function getSuggestion() {
    const res = await fetch('https://api.example/suggestion');
    const { suggestion } = await res.json();
    console.log(suggestion);
}

10行 → 3行。

使用async/await,我們終于可以為異步代碼使用原生的 try-catch:

? ES8之前:

startWorkout();

function startWorkout() {
    goToGym()
        .then((result) => {
            console.log(result);
        })
        .catch((err) => {
            console.log(err);
        });
}

function goToGym() {
    return new Promise((resolve, reject) => {
        if (Math.random() > 0.5) {
            reject(new Error("I'm tired today!??"));
        }
        resolve("Let's go!??♂?");
    });
}

? 現(xiàn)在:

startWorkout();

// ? async/await
async function startWorkout() {
    try {
        await goToGym();
    } catch (err) {
        console.log(err);
    }
}

function goToGym() {
    return new Promise((resolve, reject) => {
        if (Math.random() > 0.5) {
            reject(new Error("I'm tired today!??"));
        }
        resolve("Let's go!??♂?");
    });
}

3.強大的Object靜態(tài)方法

Object.values()

一個出色的靜態(tài)方法,可以將對象的所有值提取到一個數(shù)組中:

const person = {
    name: 'Tari Ibaba',
    site: 'codingbeautydev.com',
    color: '??blue',
};

const arr = Object.values(person);

// ['Tari Ibaba', 'codingbeautydev.com', '??blue']
console.log(arr);

非常適合數(shù)據(jù)可視化:

const fruits = [
    {
        name: 'Banana',
        pic: '??',
        color: 'yellow',
    },
    {
        name: 'Apple',
        pic: '??',
        color: 'red',
    },
];

const keys = Object.keys(fruits.at(0));
const header = keys.map((key) => `| ${key} |`).join('');
const rows = fruits
    .map((fruit) =>
        keys.map((key) => `| ${fruit[key]} |`).join('')
    ).join('\n');

console.log(header + '\n' + rows);

圖片圖片

Object.entries()

const person = {
    name: 'Tari Ibaba',
    site: 'codingbeautydev.com',
    color: '??blue',
};

const arr = Object.entries(person);

/*
[
  ['name', 'Tari Ibaba'],
  ['site', 'codingbeautydev.com'],
  ['color', '??blue']
]
*/

console.log(arr);

將對象中的每個鍵值對捆綁在一起,生成一個元組列表:

非常適合使用對象的鍵和值進行數(shù)據(jù)轉換:

以ID為鍵的對象 → 對象列表:

? 之前:

const tasks = {
    1: {
        title: '???HIIT 30 minutes today',
        complete: false,
    },
    2: {
        name: 'Buy the backpack??',
        complete: true,
    },
};

const taskList = Object.keys(tasks).map((id) => ({
    id,
    ...tasks[id],
}));

console.log(taskList);

? 現(xiàn)在:

// ? 更簡潔
const taskList = Object.entries(tasks).map(
    ([id, task]) => ({
        id,
        ...task,
    })
);

console.log(taskList);

圖片圖片

4.原生字符串填充

2016年3月22日,流行的NPM包left-pad被創(chuàng)建者作為一種抗議形式下架,導致數(shù)千個軟件項目崩潰。

這讓許多人擔心我們可能過度依賴外部模塊 — 即使是像字符串填充這樣簡單的功能。

但幸運的是,ES8為JavaScript帶來了原生的字符串填充功能,即padStart和padEnd字符串方法:

const name = 'tari';

console.log(name.padStart(9, ' '));    // '     tari'
console.log(name.padEnd(10, '??')); // 'tari????????'

我們不再需要依賴另一個第三方依賴。

5. Object.getOwnPropertyDescriptors()

名字聽起來有點花哨,但很容易理解。

描述符是屬性的屬性 — 以下之一:

  • value
  • enumerable
  • get
  • set
  • configurable
  • enumerable
const person = {
  name: 'Tari Ibaba',
  color: '??color',
  age: 999,
  greet: () => console.log('Hey!'),
};

console.log(
  Object.getOwnPropertyDescriptors(person)
);

圖片圖片

最后的思考

總的來說,ES8對JavaScript來說是一個重大飛躍,引入了幾個已成為現(xiàn)代開發(fā)必不可少的特性。使你能夠編寫更簡潔、更富表現(xiàn)力和更清晰的代碼。

過去10年里,JavaScript取得了長足進步,每年都有全新的功能升級。

今天,我們來看看早期ES9中引入的5個最重要的特性,看看你是否錯過了其中一些。

1. 異步生成器和迭代

異步生成器是ES9中一個強大的特性。

就像普通的生成器,但現(xiàn)在它可以在異步工作(如網(wǎng)絡請求)后彈出值:

function* asyncGenerator() {
  yield new Promise((resolve) =>
    setTimeout(() => resolve('done this ?'), 2000)
  );
  yield new Promise((resolve) =>
    setTimeout(() => resolve('done that ?'), 3000)
  );
}

當我們調用.next()時,我們會得到一個Promise:

const asyncGen = asyncGenerator();

asyncGen.next().value.then(console.log);
asyncGen.next().value.then(console.log);

這是一個強大的工具,可以在web應用中以結構化+可讀的方式流式傳輸數(shù)據(jù) — 看看這個為類似YouTube的視頻分享應用緩沖和流式傳輸數(shù)據(jù)的函數(shù):

async function* streamVideo({ id }) {
  let endOfVideo = false;
  const downloadChunk = async (sizeInBytes) => {
    const response = await fetch(
      `api.example.com/videos/${id}`
    );
    const { chunk, done } = await response.json();
    if (done) endOfVideo = true;
    return chunk;
  };
  while (!endOfVideo) {
    const bufferSize = 500 * 1024 * 1024;
    yield await downloadChunk(bufferSize);
  }
}

現(xiàn)在要消費這個生成器,我們將使用for await of — 異步迭代:

for await (const chunk of streamVideo({ id: 2341 })) {
  // process video chunk
}

我想知道實際的YouTube JavaScript代碼是否使用了這樣的生成器?

2.對象的剩余/展開運算符

毫無疑問,你在某處遇到過現(xiàn)代的展開語法。

這是一種快速且不可變地克隆數(shù)組的天才方法:

const colors = ['??', '??', '??'];

console.log([...colors, '??']); 
// ['??', '??', '??', '??']

在ES6之前我們從未有過它,現(xiàn)在它無處不在。

Redux就是一個重要的例子:

export default function userState(state = initialUserState, action) {
  console.log(arr); 
  switch (action.type) {
    case ADD_ITEM:
      return {
        ...state,
        arr: [...state.arr, action.newItem]
      };
    default: 
      return state;
  }
}

從ES9開始,它也適用于對象:

const info = {
  name: 'Coding Beauty',
  site: 'codingbeautydev.com',
};

console.log({ ...info, theme: '??' });

/* Output:
{
  name: 'Coding Beauty',
  site: 'codingbeautydev.com',
  theme: '??'
}
*/

覆蓋屬性:

const langs = {
  j: 'java',
  c: 'c++',
};

console.log({ ...langs, j: 'javascript' });

// Output: { j: 'javascript', c: 'c++' }

這使得它特別適合在默認值的基礎上構建,尤其是在制作公共實用程序時。

或者像我用Material UI定制默認主題那樣:

圖片圖片

使用展開語法,你甚至可以去掉不想在副本中出現(xiàn)的對象屬性。

const colors = {
  yellow: '??',
  blue: '??',
  red: '??',
};

const { yellow, ...withoutYellow } = colors;

console.log(withoutYellow);

// Output: { blue: '??', red: '??' }

這就是如何以不可變的方式從對象中移除屬性。

3. String.raw

當我使用String.raw時,我是在說:只給我我給你的東西。不要處理任何東西。不要動那些轉義字符:

不再需要轉義反斜杠,我們不用寫:

const filePath = 'C:\\Code\\JavaScript\\tests\\index.js';

console.log(`The file path is ${filePath}`);

// Output: The file path is C:\Code\JavaScript\tests\index.js

而是寫:

const filePath = String.raw`C:\Code\JavaScript\tests\index.js`;

console.log(`The file path is ${filePath}`);

// Output: The file path is C:\Code\JavaScript\tests\index.js

非常適合編寫帶有大量這些反斜杠的正則表達式:

像這樣但更糟:

從這個?:

const patternString = 'The (\\w+) is (\\d+)';
const pattern = new RegExp(patternString);

const message = 'The number is 100';

console.log(pattern.exec(message));
// ['The number is 100', 'number', '100']

到這個?:

const patternString = String.raw`The (\w+) is (\d+)`;
const pattern = new RegExp(patternString);

const message = 'The number is 100';

console.log(pattern.exec(message));
// ['The number is 100', 'number', '100']

所以"raw"意味著未處理的。

圖片圖片

這就是為什么我們有String.raw()但沒有String.cooked()。

4. 復雜的正則表達式特性

說到正則表達式,ES9并沒有讓人失望。

它完全裝載了最先進的正則表達式特性,用于高級字符串搜索和替換。

向后查找斷言

這是一個新特性,用于確保只有某個特定模式出現(xiàn)在你要搜索的內(nèi)容之前:

  • 正向后查找:白名單 ?<=pattern
  • 負向后查找:黑名單 ?<!pattern
const str = "It's just $5, and I have €20 and £50";

// Only match number sequence if $ comes first
const regexPos = /(?<=\$)\d+/g;

console.log(str.match(regexPos)); // ['5']

const regexNeg = /(?<!\$)\d+/g;

console.log(str.match(regexNeg)); // ['20', '50']

圖片圖片

命名捕獲組

捕獲組一直是正則表達式中最寶貴的特性之一,用于以復雜的方式轉換字符串。

const str = 'The cat sat on a map';

// $1 -> [a-z]
// $2 -> a
// $3 -> t

// () indicates group
str.replace(/([a-z])(a)(t)/g, '$1*$3');
// -> The c*t s*t on a map

通常,這些組按照它們在正則表達式中的相對位置命名:1, 2, 3...

但這使得理解和更改那些愚蠢的長正則表達式變得更加困難。

所以ES9通過?<name>來命名捕獲組解決了這個問題:

const str = 'The cat sat on a map';

// left & right
console.log(str.replace(/(?<left>[a-z])(a)(?<right>t)/g, '$<left>*$<right>'));

// -> The c*t s*t on a map

圖片圖片

你知道當VS Code中出現(xiàn)錯誤時,你可以快速Alt + 點擊跳轉到錯誤發(fā)生的確切位置嗎???

圖片圖片

VS Code使用捕獲組使文件名可點擊,從而實現(xiàn)這種快速導航。

我想它大概是這樣的:

// The stupidly long regex
const regex = /(?<path>[a-z]:[a-z].(?:?:\\/|(?:\\/?)))[\w \-]+):(?<line>\d+):(?<char>\d+)/gi;

// ? String.raw!
const filePoint = String.raw`C:\coding-beauty\coding-beauty-javascript\index.js:3:5`;

const extractor = /(?<path>[a-z]:[a-z].(?:?:\\/|(?:\\/?)))[\w \-]+):(?<line>\d+):(?<char>\d+)/i;
const [path, lineStr, charStr] = filePoint
  .match(regex)[0]
  .match(extractor)
  .slice(1, 4);

const line = Number(lineStr);

const char = Number(charStr);

console.log({ path, line, char });

// Replace all filePoint with <button> tag
// <button notallow="navigateWithButtonFilepointInnerText">filePoint</button>

5. Promise.finally

最后我們有了Promise.finally ??。

你知道finally總是會運行一些代碼,無論是否有錯誤嗎?

function startBodyBuilding() {
  if (Math.random() > 0.5) {
    throw new Error("I'm tired??");
  }
  console.log('Off to the gym ???♂???');
}

try {
  startBodyBuilding();
} catch {
  console.log('Stopped excuse??');
} finally {
  console.log("I'm going!??♂?");
}

所以Promise.finally就像那樣,但是用于異步任務:

async function startBodyBuilding() {
  await think();
  if (Math.random() > 0.5) {
    throw new Error("I'm tired??");
  }
  console.log('Off to the gym ???♂???');
}

startBodyBuilding()
  .then(() => {
    console.log('Started ?');
  })
  .catch(() => {
    console.log('No excuses');
  })
  .finally(() => {
    console.log("I'm going!??♂?");
  });

Promise.finally()最大的優(yōu)點是當你鏈接許多Promise時:

它也能很好地與Promise鏈一起工作:

getFruitApiUrl().then((url) => {
  return fetch(url)
    .then((res) => res.json())
    .then((data) => {
      fruits.push(data);
    })
    .catch((err) => {
      console.error(err);
    })
    .finally(() => {
      console.log(fruits);
    });
});

這是由ES9帶來的。

最后的思考

ES9標志著JavaScript的一個重大飛躍,引入了幾個對現(xiàn)代開發(fā)至關重要的特性。使你能夠快速編寫更清晰、更簡潔、更富表現(xiàn)力的代碼。

JavaScript在過去10年里取得了長足的進步,每一年都有全新的功能升級。

還記得我們以前是這樣創(chuàng)建"類"的嗎?

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log("Hello, " + this.name);
};

是的,變化很大!

讓我們來看看ES10(2019年)中引入的7個最重要的特性,看看你是否錯過了其中一些。

1. 即時模塊化:動態(tài)import

ES10那年很棒,import現(xiàn)在可以像require()一樣作為函數(shù)使用。一個async函數(shù)。

將import保持在頂層不再是必須的;我們現(xiàn)在可以在編譯時輕松解析模塊的名稱。

為了高性能,可以選擇性地只在絕對需要時加載模塊...

if (user.is_admin) {
  const admin = await import('./admin.js');
  admin.setupDashboard();
}

基于用戶或變量輸入加載模塊...

const language = 'french';
const translations = await import(`./translations/${language}.js`);

它也非常適合使用不再支持require()的ES模塊:

2. 扁平化曲線

flat()和flatMap()提供了更清晰的方式來輕松扁平化多維數(shù)組。

消除了痛苦的數(shù)組循環(huán)扁平化代碼的需求:

圖片圖片

flatMap()相當于調用map(),然后flat(1):

圖片圖片

3. 將數(shù)組轉換為對象

ES10還引入了Object.fromEntries()到JavaScript世界。

快速將鍵值對列表轉換為等效的鍵值對象:

const entries = [['name', 'John'], ['age', 30]];
const obj = Object.fromEntries(entries);
console.log(obj); // { name: 'John', age: 30 }

4. 精確清理你的字符串

trimStart()和trimEnd()。

在此之前,每個人都在使用NPM的trim - 愉快地給項目增加3.35KB...

即使現(xiàn)在:

npm i trim

然后Array trim()出現(xiàn)了,接著是trimStart()和trimEnd()。

const str = '   Hello, World!   ';
console.log(str.trimStart()); // 'Hello, World!   '
console.log(str.trimEnd());   // '   Hello, World!'

5. 捕獲錯誤而不帶包袱

通過新的可選catch綁定,當你對錯誤參數(shù)無所作為時,現(xiàn)在可以安全地省略catch塊的錯誤參數(shù):

圖片圖片

6. 無驚喜排序

穩(wěn)定的數(shù)組排序。

以前,在對數(shù)組進行排序時,我們絕對無法保證相等元素的排列。

但在ES10之后的JS代碼中,我們100%確定react總是在vue之前,vue總是在angular之前。

圖片圖片

圖片圖片

7. 要么做大,要么回家:BigInt

BigInt的名稱揭示了它的目的:用于加載難以置信的巨大整數(shù)值:

圖片圖片

圖片圖片

因為普通整數(shù)做不到:

圖片圖片

最后的思考

ES10為JavaScript標志著一個重要的飛躍,引入了幾個對現(xiàn)代開發(fā)至關重要的特性。

使用它們來編寫更清晰、更簡潔、更具表現(xiàn)力和清晰度的代碼。

ES13包含了許多有價值的特性,徹底改變了我們編寫JavaScript的方式。

從異步升級到數(shù)組語法糖等等,讓我們來看看這些特性,看看你是否錯過了其中一些。

1. 頂級await

在ES13之前,我們永遠不能在全局作用域中使用await。

? 之前:

// X 語法錯誤:await 只在異步函數(shù)中有效
await setTimeoutAsync(3000);

function setTimeoutAsync(timeout) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('codingbeautydev.com');
    }, timeout);
  });
}

我們總是必須將其放在async函數(shù)中或創(chuàng)建一個async IIFE(立即執(zhí)行函數(shù)表達式):

// 異步立即執(zhí)行函數(shù)
(async () => {
  await setTimeoutAsync(3000);
})();

// 類似 C++
async function main() {
  await setTimeoutAsync(3000);
}

? ES13之后:

// ? 等待超時 - 沒有拋出錯誤
await setTimeoutAsync(3000);

function setTimeoutAsync(timeout) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('codingbeautydev.com');
    }, timeout);
  });
}

2. 類聲明升級

2.1 類字段聲明

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

? 之前:

? 現(xiàn)在有了ES13: 就像在TypeScript中一樣:

2.2 私有方法和字段

在ES13之前,創(chuàng)建私有方法是不可能的。 我們還必須使用丑陋的下劃線hack來表示私有性 — 但那只是一個指示。

? 之前:

class Person {
  _firstName = 'Tari';
  _lastName = 'Ibaba';

  get name() {
    return `${this._firstName} ${this._lastName}`;
  }
}

const person = new Person();
console.log(person.name); // Tari Ibaba

// 我們?nèi)匀豢梢栽L問私有成員!
console.log(person._firstName); // Tari
console.log(person._lastName); // Ibaba

// 它們也可以被修改!
person._firstName = 'Lionel';
person._lastName = 'Messi';

console.log(person.name); // Lionel Messi

? ES13之后:

我們可以通過在字段前加上井號(#)來為類添加私有字段和成員:

如果你試圖從類外部訪問它,你會得到一個語法錯誤:

class Person {
  #firstName = 'Tari';
  #lastName = 'Ibaba';

  get name() {
    return `${this.#firstName} ${this.#lastName}`;
  }
}

const person = new Person();
console.log(person.name);

// 語法錯誤:私有字段 '#firstName' 必須在封閉的類中聲明
console.log(person.#firstName);
console.log(person.#lastName);

我們可以從錯誤消息中看到一些有趣的東西:

編譯器甚至不期望你從類外部嘗試訪問私有字段 — 它假設你是在嘗試創(chuàng)建一個。

2.3 靜態(tài)類字段和靜態(tài)私有方法

靜態(tài)字段 — 類本身的屬性,而不是任何特定實例的屬性。

自ES13以來,我們現(xiàn)在可以輕松地為任何類創(chuàng)建它們:

class Person {
  static #count = 0;
  static eyeCount = 2;

  static getCount() {
    // 使用 this 訪問同級靜態(tài)成員
    return this.#count;
  }

  // 實例成員
  constructor() {
    // 使用 this.constructor 訪問靜態(tài)成員
    this.constructor.#incrementCount();
  }

  static #incrementCount() {
    this.#count++;
  }
}

const person1 = new Person();
const person2 = new Person();
console.log(Person.getCount()); // 2

3. 數(shù)組升級:新的at()方法

通常我們會使用方括號([])來訪問數(shù)組的第N個元素。

const arr = ['a', 'b', 'c', 'd'];

console.log(arr[1]); // b

但從末尾訪問第N個項目一直是一個痛點 -- 我們必須使用arr.length - N進行索引:

? ES13之前:

const arr = ['a', 'b', 'c', 'd'];

// 倒數(shù)第1個元素
console.log(arr[arr.length - 1]); // d

// 倒數(shù)第2個元素
console.log(arr[arr.length - 2]); // c

幸運的是,ES13帶來了一個新的at()方法,解決了所有這些問題:

const str = 'Coding Beauty';

console.log(str.at(-1)); // y 倒數(shù)第1個字符

console.log(str.at(-2)); // t 倒數(shù)第2個字符

4. 靜態(tài)類塊

隨著靜態(tài)字段的出現(xiàn),靜態(tài)塊也來了。 只在類創(chuàng)建時執(zhí)行一次代碼 — 就像C#和Java等OOP語言中的靜態(tài)構造函數(shù)。 所以你可以在類中創(chuàng)建任意多個靜態(tài)塊 — 所有代碼都會按你定義的順序運行:

class Vehicle {
  static defaultColor = 'blue';
}

class Car extends Vehicle {
  static colors = [];
  // ?? pushes red before green
  // ?? 先添加 red,然后添加 green
  static {
    this.colors.push(super.defaultColor, 'red');
  }
  static {
    this.colors.push('green');
  }
}

console.log(Car.colors); // ['blue', 'red', 'green']

5. 錯誤報告升級

有時我們捕獲調用棧下方方法的錯誤,只是為了將其重新拋出回調用棧上方。 但當我們這樣做時,我們會失去原始錯誤中的關鍵信息:

try {
  userAction();
} catch (err) {
  // ? doesn't know fundamental cause of error
  // ? 不知道錯誤的根本原因
  console.log(err);
}

function userAction() {
  try {
    apiCallThatCanThrow();
  } catch (err) {
    // ?? rethrow
    // ?? 重新拋出錯誤
    throw new Error('New error message');
  }
}

function apiCallThatCanThrow() {
  console.log('fetching from codingbeautydev.com...');
  throw new Error('throwing for no reason');
}

這就是為什么ES13引入了一個新的cause屬性來保留這個重要信息并使調試更容易:

try {
  userAction();
} catch (err) {
  // ? now knows what caused the error
  // ? 現(xiàn)在知道了錯誤的原因
  console.log(err);
  console.log(`Caused by: ${err.cause}`);
}

function userAction() {
  try {
    apiCallThatCanThrow();
  } catch (err) {
    // ? error cause
    // ? 錯誤原因
    throw new Error('New error message', { cause: err });
  }
}

function apiCallThatCanThrow() {
  console.log('fetching from codingbeautydev.com...');
  throw new Error('throwing for no reason');
}

最后的思考

總的來說,ES13對JavaScript來說是一個重大飛躍,它帶來了幾個已成為現(xiàn)代開發(fā)必不可少的特性。 使你能夠編寫更清晰、更簡潔、更具表現(xiàn)力的代碼。

JavaScript在過去10年里取得了長足的進步,每年都有全新的功能升級。 讓我們來看看ES14(2023年)中引入的5個最重要的特性,看看你是否錯過了其中一些。

1. toSorted()

甜美的語法糖。

ES14的toSorted()方法使得排序數(shù)組并返回一個副本而不改變原數(shù)組變得更加容易。

以前我們這樣做:

const numbers = [3, 1, 4, 1, 5];
const sorted = [...numbers].sort((a, b) => a - b);
console.log(sorted); // [1, 1, 3, 4, 5]
console.log(numbers); // [3, 1, 4, 1, 5]

現(xiàn)在我們可以這樣做?:

const numbers = [3, 1, 4, 1, 5];
const sorted = numbers.toSorted((a, b) => a - b);
console.log(sorted); // [1, 1, 3, 4, 5]
console.log(numbers); // [3, 1, 4, 1, 5]

toSorted()接受一個回調函數(shù)來控制排序行為 - 升序或降序,按字母順序或數(shù)字順序。就像sort()一樣。

2. toReversed()

另一個新的數(shù)組方法,用于促進不可變性和函數(shù)式編程。

以前 — 使用reverse() ?:

const numbers = [1, 2, 3, 4, 5];
const reversed = numbers.reverse();
console.log(reversed); // [5, 4, 3, 2, 1]
console.log(numbers); // [5, 4, 3, 2, 1]

現(xiàn)在 — 使用toReversed() ?:

const numbers = [1, 2, 3, 4, 5];
const reversed = numbers.toReversed();
console.log(reversed); // [5, 4, 3, 2, 1]
console.log(numbers); // [1, 2, 3, 4, 5]

我發(fā)現(xiàn)這些不可變方法非常棒,可以不斷地鏈式調用方法,而不用擔心原始變量:

const result = numbers.toReversed().toSorted((a, b) => a - b);

3. toSpliced()

函數(shù)式編程愛好者無疑會對所有這些新的數(shù)組方法感到高興。 這是.splice()的不可變版本:

const items = [1, 2, 3, 4, 5];
const newItems = items.toSpliced(2, 1, 6, 7);
console.log(newItems); // [1, 2, 6, 7, 4, 5]
console.log(items); // [1, 2, 3, 4, 5]

4. 從末尾開始查找數(shù)組

從第一項開始搜索并不總是理想的:

圖片圖片

你可以很容易地看到,對我們的巨大列表從末尾而不是開始搜索會快得多。

圖片圖片

有時你必須從末尾搜索才能讓你的程序工作。

比如我們想在一個數(shù)字列表中找到最后一個偶數(shù),find和findIndex會非常不準確。 調用reverse()也不行,即使它會很慢:

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const lastEven = numbers.reverse().find(n => n % 2 === 0);
console.log(lastEven); // 10(不正確)

所以在這種情況下,findLast()和findLastIndex()方法就派上用場了。

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const lastEven = numbers.findLast(n => n % 2 === 0);
console.log(lastEven); // 10(正確)

這段代碼更短、更易讀。最重要的是,它產(chǎn)生了正確的結果。

5. 數(shù)組的with()方法

with()是我們快速更改數(shù)組元素而不進行任何突變的方法。

以前的常規(guī)方式:

const arr = [1, 2, 3, 4, 5];
const newArr = [...arr];
newArr[2] = 6;
console.log(newArr); // [1, 2, 6, 4, 5]
console.log(arr); // [1, 2, 3, 4, 5]

ES14現(xiàn)在讓我們這樣做:

const arr = [1, 2, 3, 4, 5];
const newArr = arr.with(2, 6);
console.log(newArr); // [1, 2, 6, 4, 5]
console.log(arr); // [1, 2, 3, 4, 5]

最后的思考

還有其他特性,但ES14主要是關于更容易的函數(shù)式編程和內(nèi)置的不可變性。 隨著React的興起,我們看到聲明式JavaScript爆炸式地流行起來;很自然地,更多的這些特性會被烘焙到語言中,成為甜美的語法糖。

2024年:又是一個帶來全新JS特性升級的不可思議的年份,ES15推出。

從復雜的異步特性到語法糖數(shù)組和現(xiàn)代正則表達式,JavaScript編碼現(xiàn)在比以往任何時候都更簡單、更快捷。

1.原生數(shù)組分組終于到來

Object.groupBy():

const fruits = [  { name: 'pineapple??', color: '??' },  { name: 'apple??', color: '??' },  { name: 'banana??', color: '??' },  { name: 'strawberry??', color: '??' },];const groupedByColor = Object.groupBy(  fruits,  (fruit, index) => fruit.color);// 原生 group by 示例console.log(groupedByColor);

圖片圖片

字面意思就是讓恐龍級的 Lodash 庫失去了最后的存在理由 - 再也不需要了!

圖片圖片

我原本期待一個新的實例方法,比如Array.prototype.groupBy,但不知什么原因他們把它做成了靜態(tài)方法。

然后我們還有Map.groupBy來用對象鍵進行分組:

const array = [1, 2, 3, 4, 5];const odd = { odd: true };const even = { even: true };Map.groupBy(array, (num, index) => {  return num % 2 === 0 ? even : odd;});// => Map { {odd: true}: [1, 3, 5], {even: true}: [2, 4] }

不過幾乎沒人會這樣對數(shù)組分組,所以可能不會那么受歡迎。

2.從外部解決promise - 現(xiàn)代方式

使用Promise.withResolvers()。

從外部解決promises是很普遍的需求,在此之前我們不得不使用Deferred類來實現(xiàn):

class Deferred {  constructor() {    this.promise = new Promise((resolve, reject) => {      this.resolve = resolve;      this.reject = reject;    });  }}const deferred = new Deferred();deferred.resolve();

或者從NPM安裝 - 又多了一個依賴!

圖片圖片

但現(xiàn)在有了ES15的Promise.withResolvers():

const { promise, resolve, reject } = Promise.withResolvers();

看看我如何用它來快速地將事件流promise化 - await一個observable:

// data-fetcher.jsconst { promise, resolve, reject } = Promise.withResolvers();function startListening() {    eventStream.on('data', (data) => {        resolve(data);    });}async function getData() {    return await promise;}// client.jsconst { startListening, getData } = require('./data-fetcher.js');startListening();// ? 監(jiān)聽單個流事件const data = await getData();

3. Buffer性能升級

Buffers是用來存儲應用程序生成的臨時數(shù)據(jù)的小型數(shù)據(jù)存儲。

它們使得在管道的各個階段之間傳輸和處理數(shù)據(jù)變得非常容易。

像這樣的管道:

  • 文件處理: 輸入文件 → buffer → 處理 → 新buffer → 輸出文件
  • 視頻流: 網(wǎng)絡響應 → buffer → 顯示視頻幀
  • 餐廳隊列: 接待顧客 → 隊列/buffer → 服務顧客
const fs = require('fs');const { Transform } = require('stream');const inputFile = 'input.txt';const outputFile = 'output.txt';const inputStream = fs.createReadStream(inputFile, 'utf-8');const transformStream = new Transform({    transform(chunk) {        // ? 從緩沖區(qū)轉換塊    },});const outputStream = fs.createWriteStream(outputFile);// ? 開始管道inputStream.pipe(transformStream).pipe(outputStream);

使用 buffers,每個階段可以以不同的速度獨立處理數(shù)據(jù)。

但是當通過管道移動的數(shù)據(jù)超過buffer容量時會發(fā)生什么?

以前我們必須將當前所有數(shù)據(jù)的buffer復制到一個更大的buffer中。

這對性能來說很糟糕,尤其是當管道中將有大量數(shù)據(jù)時。

ES15為我們提供了解決這個問題的方案:可調整大小的數(shù)組buffers。

const resizableBuffer = new ArrayBuffer(1024, {    maxByteLength: 1024 ** 2,});// ? 調整大小到 2048 字節(jié)resizableBuffer.resize(1024 * 2);

4.異步升級

Atomics.waitAsync(): ES2024中另一個強大的異步編碼特性:

它是當2個代理共享一個buffer時...

代理1"睡眠"并等待代理2完成任務。

當代理2完成時,它使用共享buffer作為通道進行通知。

const sharedBuffer = new SharedArrayBuffer(4096);const bufferLocation = new Int32Array(sharedBuffer);// 初始化緩沖區(qū)位置的初始值bufferLocation[37] = 0x1330;async function doStuff() {    // ? agent 1:在共享緩沖區(qū)位置等待直到通知    Atomics.waitAsync(bufferLocation, 37, 0x1330).then(        (r) => { /* 處理到達 */ }    );}function asyncTask() {    // ? agent 2:在共享緩沖區(qū)位置通知    const bufferLocation = new Int32Array(sharedBuffer);    Atomics.notify(bufferLocation, 37);}

如果你認為這類似于普通的async/await,你絕對是對的。

但最大的區(qū)別是:這2個代理可以存在于完全不同的代碼上下文中 - 它們只需要訪問相同的buffer。

而且:多個代理可以在不同時間訪問或等待共享buffer - 其中任何一個都可以通知"喚醒"所有其他代理。

這就像P2P網(wǎng)絡;而async/await更像是客戶端-服務器請求-響應模式。

const sharedBuffer = new SharedArrayBuffer(4096);const bufferLocation = new Int32Array(sharedBuffer);bufferLocation[37] = 0x1330;// ? 從 postMessage() 接收到的共享緩沖區(qū)const code = `var ia = null;onmessage = function (ev) {    if (!ia) {        postMessage("Aux worker is running");        ia = new Int32Array(ev.data);    }    postMessage("Aux worker is sleeping for a little bit");    setTimeout(function () { postMessage("Aux worker is waking"); Atomics.notify(ia, 37); }, 1000);};`;async function doStuff() {    // ? agent 1:存在于 Worker 上下文中    const worker = new Worker(        'data:application/javascript,' + encodeURIComponent(code)    );    worker.onmessage = (event) => {        // 記錄事件    };    worker.postMessage(sharedBuffer);    Atomics.waitAsync(bufferLocation, 37, 0x1330).then(        (r) => { /* 處理到達 */ }    );}function asyncTask() {    // ? agent 2:在共享緩沖區(qū)位置通知    const bufferLocation = new Int32Array(sharedBuffer);    Atomics.notify(bufferLocation, 37);}

5.正則表達式v標志和集合操作

這是一個全新的特性,使正則表達式更加清晰和直觀。

使用表達式模式查找和操作復雜字符串 - 在集合操作的幫助下:

// A 和 B 是字符類,如 [a-z]// 差異:匹配 A 但不匹配 B[A--B]// 交集:同時匹配 A 和 B[A&&B]// 嵌套字符類[A--[0-9]]

匹配不斷增加的Unicode字符集,如:

  • 表情符號: ??, ??, ??, ??, 等
  • 重音字母: é, à, ?, ?, 等
  • 符號和非拉丁字符: ?, ?, €, £, μ, ¥, 等

所以這里我們使用Unicode正則表達式和v標志來匹配所有希臘字母:

const regex = /[\p{Script_Extensinotallow=Greek}&&\p{Letter}]/v;

最后的想法

總的來說,ES15對JavaScript來說是一個重大飛躍,包含了幾個對現(xiàn)代開發(fā)至關重要的特性。幫助你以更簡潔、更富表現(xiàn)力、更清晰的方式編寫更干凈的代碼。

責任編輯:武曉燕 來源: 大遷世界
相關推薦

2023-12-18 23:09:25

開源優(yōu)化引擎

2024-07-30 08:40:00

2024-01-29 00:29:49

通信技術行業(yè)

2021-06-08 22:43:07

IPC方式Qt

2024-08-01 08:38:59

2024-08-12 08:36:28

2024-07-25 08:37:48

2020-03-31 08:12:25

Kafka架構數(shù)據(jù)庫

2024-06-14 10:22:55

2021-12-06 08:30:49

SpringSpring Bean面試題

2025-03-04 10:03:47

2021-03-29 12:22:25

微信iOS蘋果

2024-08-16 09:14:53

2020-10-21 06:39:21

CPU寄存器架構

2020-10-22 12:30:33

MySQL

2020-09-24 09:08:04

分布式系統(tǒng)架構

2020-04-14 13:32:56

@Transacti失效場景

2022-05-24 11:50:46

延時消息分布式

2021-03-01 18:52:39

工具在線瀏覽器

2021-05-18 09:03:16

Gomapslice
點贊
收藏

51CTO技術棧公眾號