<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ECMAScript 2023:為JavaScript帶來新的數(shù)組復制方法

          共 10525字,需瀏覽 22分鐘

           ·

          2023-11-09 16:39


          大廠技術  高級前端  Node進階

          點擊上方 程序員成長指北,關注公眾號

          回復1,加入高級Node交流群

          整理 | 丁曉昀、核子可樂

          ECMAScript 2023 規(guī)范最近已經(jīng)定稿,其中提出的 Array 對象新方法將為 JavaScript 帶來更好的可預測性和可維護性。toSorted、toReversed、toSpliced 和 with 方法允許用戶在不更改數(shù)據(jù)的情況下對數(shù)據(jù)執(zhí)行操作,實質是先制造副本再更改該副本。

          變異與副作用

          Array 對象總是有點自我分裂。sort、reverse 和 splice 等方法會就地更改數(shù)組,concat、map 和 filter 等其他方法則是先創(chuàng)建數(shù)組副本,再對副本執(zhí)行操作。當我們通過操作讓對象產(chǎn)生變異時,則會產(chǎn)生一種副作用,導致系統(tǒng)其他位置發(fā)生意外行為。

          舉例來說,當 reverse 一個數(shù)組時會發(fā)生如下情況。

          const languages = ["JavaScript", "TypeScript", "CoffeeScript"];const reversed = languages.reverse();console.log(reversed);// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]console.log(languages);// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]console.log(Object.is(languages, reversed));// => true

          可以看到,原始數(shù)組已經(jīng)反轉,但即使我們將反轉數(shù)組的結果分配給一個新變量,兩個變量也仍指向同一數(shù)組。

          變異數(shù)組和 React

          數(shù)組變異方法中一個最著名的問題,就是在 React 組件中使用時的異常。我們無法變異數(shù)組,之后嘗試將其設置為新狀態(tài),因為數(shù)組本身是同一個對象且不會觸發(fā)新的渲染。相反,我們需要先復制該數(shù)組,然后改變副本再將其設置為新狀態(tài)。因此,React 文檔專門有一整頁解釋了如何更新狀態(tài)數(shù)組。

          先復制,后變異

          解決這個問題的方法,是先復制數(shù)組,之后再執(zhí)行變異。我們可以通過幾種不同方法來生成數(shù)組副本,包括:Array.from,展開運算符,或者調用不帶參數(shù)的 slice 函數(shù)。

          const languages = ["JavaScript", "TypeScript", "CoffeeScript"];const reversed = Array.from(languages).reverse();// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]console.log(languages);// => [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]console.log(Object.is(languages, reversed));// => false

          有辦法能解決當然很好,總之請千萬注意不同復制操作間是有區(qū)別的。

          新方法可隨副本變化

          此次公布的新方法正是為此而生。toSorted、toReversed、toSpliced 和 with 都能復制原始數(shù)組、變更副本再返回結果。如此一來,每項操作都更易于編寫,開發(fā)者只需調用一個函數(shù)即可,代碼閱讀起來也更容易、不必預先考慮到底要用具體哪種數(shù)組復制方法。下面,我們來看這幾種新方法的區(qū)別。

          Array.prototype.toSorted

          其中 toSorted 函數(shù)會返回一個新的、經(jīng)過排序的數(shù)組。

          const languages = ["JavaScript", "TypeScript", "CoffeeScript"];const sorted = languages.toSorted();console.log(sorted);// => [ 'CoffeeScript', 'JavaScript', 'TypeScript' ]console.log(languages);// => [ 'JavaScript''TypeScript''CoffeeScript' ]

          除了復制之外,sort 函數(shù)還會引發(fā)一些意想不到的行為,toSorted 也繼承了這種特點。所以在對帶有重音字符的數(shù)字或字符串進行排序時,大家仍然要小心。比如準備一個 comparator 比較器函數(shù)(例如 String's localeCompare)來生成當前查找的結果。

          const numbers = [5, 3, 10, 7, 1];const sorted = numbers.toSorted();console.log(sorted);// => [ 1, 10, 3, 5, 7 ]const sortedCorrectly = numbers.toSorted((a, b) => a - b);console.log(sortedCorrectly);// => [ 1, 3, 5, 7, 10 ]
          const strings = ["abc", "?bc", "def"];const sorted = strings.toSorted();console.log(sorted);// => [ 'abc', 'def', '?bc' ]const sortedCorrectly = strings.toSorted((a, b) => a.localeCompare(b));console.log(sortedCorrectly);// => [ 'abc', '?bc', 'def' ]
          Array.prototype.toReversed

          使用 toReversed 函數(shù),會返回一個按相反順序排序的新數(shù)組。

          const languages = ["JavaScript", "TypeScript", "CoffeeScript"];const reversed = languages.toReversed();console.log(reversed);// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]
          之前將 reverse 的結果分配給新變量時會出問題,因為原始數(shù)組也發(fā)生了變異。但現(xiàn)在,大家可以使用 toReversed 或者 toSorted 來復制數(shù)組并更改副本。
          Array.prototype.toSpliced

          toSpliced 函數(shù)與原始版本的 splice 略有不同。splice 是在提供的索引處刪除和添加元素來更改現(xiàn)有數(shù)組,再返回一個包含數(shù)組中所刪除元素的數(shù)組。toSpliced 則直接返回一個新數(shù)組,其中不含被刪除的元素,且包含所添加的元素。其工作方式如下:

          const languages = ["JavaScript", "TypeScript", "CoffeeScript"];const spliced = languages.toSpliced(2, 1, "Dart", "WebAssembly");console.log(spliced);// => [ 'JavaScript''TypeScript''Dart''WebAssembly' ]

          如果我們使用 splice 作為返回值,那么 toSpliced 就不能直接作為替代使用。換言之,如果大家想在不改變原始數(shù)組的情況下知曉被刪除的元素是什么,就應使用 slice 復制方法。

          更麻煩的是,splice 和 slice 使用的參數(shù)也有不同。splice 使用的是一個索引加該索引之后待刪除的元素數(shù)量;slice 則使用兩個索引,分別對應開始和結束。如果要使用 toSpliced 代替 splice,但又想獲取被刪除的元素,則可對原始數(shù)組應用 toSpliced 和 slice,如下所示:

          const languages = ["JavaScript", "TypeScript", "CoffeeScript"];const startDeletingAt = 2;const deleteCount = 1;const spliced = languages.toSpliced(startDeletingAt, deleteCount, "Dart", "WebAssembly");const removed = languages.slice(startDeletingAt, startDeletingAt + deleteCount);console.log(spliced);// => [ 'JavaScript', 'TypeScript', 'Dart', 'WebAssembly' ]console.log(removed);// => [ 'CoffeeScript' ]
          Array.prototype.with

          with 函數(shù)所代表的復制方法,等同于使用方括號表示方來更改數(shù)組內的一個元素。因此,與其通過以下方式直接更改數(shù)組:

          const languages = ["JavaScript", "TypeScript", "CoffeeScript"];languages[2] = "WebAssembly";console.log(languages);// => [ 'JavaScript''TypeScript''WebAssembly' ]

          可以復制該數(shù)組再執(zhí)行更改:

          const languages = ["JavaScript", "TypeScript", "CoffeeScript"];const updated = languages.with(2, "WebAssembly");console.log(updated);// => [ 'JavaScript', 'TypeScript', 'WebAssembly' ]console.log(languages);// => [ 'JavaScript''TypeScript', CoffeeScript' ]
          不只是數(shù)組

          此次發(fā)布的新方法不僅適用于常規(guī)的數(shù)組對象。您可以在任意 TypedArray 上使用 toSorted、toReversed 和 with 方法,包括 Int8Array 到 BigUint64Array 等各種類型。但因為 TypedArrays 沒有 splice 方法,因此無法使用 toSpliced 方法。

          注意事項

          前文提到,map、filter 和 concat 等方法也都采取先復制再更改的思路,但這些方法與新的復制方法間仍有不同。如果對內置的 Array 對象進行擴展,并在實例上使用 map、flatMap、filter 或 concat,則會返回相同類型的新實例。但如果您擴展一個 Array 并使用 toSorted、toReversed、toSpliced 或者 with,則返回的仍是普通 Array。

          class MyArray extends Array {}const languages = new MyArray("JavaScript", "TypeScript", "CoffeeScript");const upcase = languages.map(language => language.toUpperCase());console.log(upcase instanceof MyArray);// => trueconst reversed = languages.toReversed();console.log(reversed instanceof MyArray);// => false

          可以使用 MyArray.from 將其轉回您的自定義 Array:

          class MyArray extends Array {}const languages = new MyArray("JavaScript", "TypeScript", "CoffeeScript");const reversed = MyArray.from(languages.toReversed());console.log(reversed instance of MyArray);// => true
          支持

          雖然 ECMAScript 2023 的規(guī)范剛剛成形,但已經(jīng)為本文提到的新數(shù)組方法提供了良好支持。Chrome 110、Safari 16.3、Node.js 20 和 Deno1.31 都支持這四種新方法,尚不支持的平臺也有 polyfills 和 shims 作為過渡方案。

          JavaScript 仍在不斷改進

          很高興看到 ECMAScript 標準新增了這么多有意義的內容,讓我們能輕松編寫出可預測性更好的代碼。其他一些提案也已被納入 ES2023,感興趣的朋友可以移步此處:https://github.com/tc39/proposals/blob/HEAD/finished-proposals.md

          至于未來的規(guī)范發(fā)展方向,推薦大家參考整個 TC39 提案庫:https://github.com/tc39/proposals

          附錄:ES2023 新特性概述
          數(shù)組倒序查找

          Array.prototype.findLast 和 Array.prototype.findLastIndex

          let nums = [5,4,3,2,1];let lastEven = nums.findLast((num) => num % 2 === 0); // 2let lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0); // 3
          Hashbang 語法
          #! for JS

          此腳本的第一行以 #!開頭,表示可在注釋中包含任意文本。

          #!/usr/bin/env node// in the Script Goal'use strict';console.log(1);
          將符號作為 WeakMap 鍵
          在弱集合和注冊表中使用符號

          注意:注冊的符號不可作為 weakmap 鍵。

          let sym = Symbol("foo");let obj = {name: "bar"};let wm = new WeakMap();wm.set(sym, obj);console.log(wm.get(sym)); // {name: "bar"}
          sym = Symbol("foo");let ws = new WeakSet();ws.add(sym);console.log(ws.has(sym)); // true
          sym = Symbol("foo");let wr = new WeakRef(sym);console.log(wr.deref()); // Symbol(foo)
          sym = Symbol("foo");let cb = (value) => {  console.log("Finalized:", value);};let fr = new FinalizationRegistry(cb);obj = {name: "bar"};fr.register(obj, "bar", sym);fr.unregister(sym);
          通過副本更改數(shù)組
          返回更改后的 Array 和 TypeArray 副本。

          注意:類型數(shù)組不可 tospliced。

          const greek = ['gamma', 'aplha', 'beta']greek.toSorted(); // [ 'aplha', 'beta', 'gamma' ]greek; // [ 'gamma', 'aplha', 'beta' ]const nums = [0, -1, 3, 2, 4]nums.toSorted((n1, n2) => n1 - n2); // [-1,0,2,3,4]nums; // [0, -1324]
          const greek = ['gamma', 'aplha', 'beta']greek.toReversed(); // [ 'beta', 'aplha', 'gamma' ]greek; // [ 'gamma''aplha''beta' ]
          const greek = ['gamma', 'aplha', 'beta']greek..toSpliced(1,2); // [ 'gamma' ]greek; // [ 'gamma', 'aplha', 'beta' ]greek.toSpliced(1,2, ...['delta']); // [ 'gamma', 'delta' ]greek; // [ 'gamma''aplha''beta' ]
          const greek = ['gamma', 'aplha', 'beta']greek..toSpliced(1,2); // [ 'gamma' ]greek; // [ 'gamma', 'aplha', 'beta' ]greek.toSpliced(1,2, ...['delta']); // [ 'gamma', 'delta' ]greek; // [ 'gamma''aplha''beta' ]
          const greek = ['gamma', 'aplha', 'beta'];greek.with(2, 'bravo'); // [ 'gamma', 'aplha', 'bravo' ]greek; //  ['gamma''aplha''beta'];

          參考鏈接:https://h3manth.com/ES2023/

          Node 社群

               
               


          我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學習感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關的交流、學習、共建。下方加 考拉 好友回復「Node」即可。

             “分享、點贊、在看” 支持一下


          瀏覽 1973
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人777777 | 国产69精品久久久久久久久久 | 中文字幕亚洲视频 | 日韩五月天在线 | 亚洲激情乱伦 |