25個JavaScript數組方法代碼示例

来源:https://www.cnblogs.com/fundebug/archive/2019/09/20/understand-25-javascript-array-methods-by-implementing-them.html
-Advertisement-
Play Games

摘要: 通過代碼掌握數組方法。 原文: "通過實現25個數組方法來理解及高效使用數組方法(長文,建議收藏)" 譯者:前端小智 "Fundebug" 經授權轉載,版權歸原作者所有。 要在給定數組上使用方法,只需要通過 即可,這些方法都定義在 對象上。在這裡,咱們先不使用這些相,反,咱們將從簡單的方法開 ...


摘要: 通過代碼掌握數組方法。

Fundebug經授權轉載,版權歸原作者所有。

要在給定數組上使用方法,只需要通過[].方法名即可,這些方法都定義在 Array.prototype 對象上。在這裡,咱們先不使用這些相,反,咱們將從簡單的方法開始定義自己的版本,併在這些版本的基礎上進行構建。

沒有比把東西拆開再重新組裝起來更好的學習方法了。註意,當咱們的實現自己的方法時,不要覆蓋現有的方法,因為有的庫需要它們,並且這樣也方便比較咱們自己的方法與原始方法的差異。

所以不要這樣命名咱們自定義的方法:

    Array.prototype.map = function map() {
     // implementation
    };

最好這樣命名:

    function map(array) {
     // implementation
    }

咱們也可以通過使用class關鍵字並擴展Array構造函數來實現咱們的方法,如下所示:

    class OwnArray extends Array {
     public constructor(...args) {
       super(...args);
     }
    
     public map() {
       // implementation
       return this;
     }
    }

唯一的區別是,我們不使用數組參數,而是使用this關鍵字。

但是,我覺得 class 方式帶來不必要的混亂,所以咱們採用第一種方法。

有了這個,咱們先從實現最簡單的方法 forEach 開始!

集合類

.forEach

Array.prototype.forEach 方法對數組的每個元素執行一次提供的函數,而且不會改變原數組。

    [1, 2, 3, 4, 5].forEach(value => console.log(value));

實現

    function forEach(array, callback) {
      const { length } = array;
      
      for (let index = 0; index < length; index += 1) {
        const value = array[index];
        callback(value, index, array)
      }
    }

咱們遍曆數組併為每個元素執行回調。這裡需要註意的一點是,該方法沒有返回什麼,所以預設返回undefined

方法漣

使用數組方法的好處是可以將操作鏈接在一起。考慮以下代碼:

    function getTodosWithCategory(todos, category) {
     return todos
       .filter(todo => todo.category === category)
       .map(todo => normalizeTodo(todo));
    }

這種方式,咱們就不必將map的執行結果保存到變數中,代碼會更簡潔。

不幸的是,forEach沒有返回原數組,這意味著咱們不能做下麵的事情

    // 無法工作
    function getTodosWithCategory(todos, category) {
     return todos
       .filter(todo => todo.category === category)
       .forEach((value) => console.log(value))
       .map(todo => normalizeTodo(todo));
    }

幫助函數 (列印信息)

接著實現一個簡單的函數,它能更好地解釋每個方法的功能:接受什麼作為輸入,返回什麼,以及它是否對數組進行了修改。

    function logOperation(operationName, array, callback) {
     const input = [...array];
     const result = callback(array);
    
     console.log({
       operation: operationName,
       arrayBefore: input,
       arrayAfter: array,
       mutates: mutatesArray(input, array), // shallow check
       result,
     });
    }

其中 mutatesArray 方法用來判斷是否更改了原數組,如果有修改剛返回 true,否則返回 false。當然大伙有好的想法可以在評論提出呦。

    function mutatesArray(firstArray, secondArray) {
      if (firstArray.length !== secondArray.length) {
        return true;
      }
    
      for (let index = 0; index < firstArray.length; index += 1) {
        if (firstArray[index] !== secondArray[index]) {
          return true;
        }
      }
    
      return false;
    }

然後使用logOperation來測試咱們前面自己實現的 forEach方法。

    logOperation('forEach', [1, 2, 3, 4, 5], array => forEach(array, value => console.log(value)));

列印結果:

    {
      operation: 'forEach',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: undefined
    }

.map

map 方法會給原數組中的每個元素都按順序調用一次 callback 函數。callback 每次執行後的返回值(包括 undefined)組合起來形成一個新數組。

實現

    function map(array, callback) {
      const result = [];
      const { length } = array;
      
      for (let index = 0; index < length; index +=1) {
        const value = array[index];
        
        result[index] = callback(value, index, array);
      }
    
      return result;
    }

提供給方法的回調函數接受舊值作為參數,並返回一個新值,然後將其保存在新數組中的相同索引下,這裡用變數 result 表示。

這裡需要註意的是,咱們返回了一個新的數組,不修改舊的。

測試

    logOperation('map', [1, 2, 3, 4, 5], array => map(array, value => value + 5));

列印結果:

    { 
      operation: 'map',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: [ 6, 7, 8, 9, 10 ]
     }

.filter

Array.prototype.filter 過濾回調返回為false的值,每個值都保存在一個新的數組中,然後返回。

    [1, 2, 3, 4, 5].filter(number => number >= 3);
    // -> [3, 4, 5]

實現

    function push(array, ...values) {
      const { length: arrayLength } = array;
      const { length: valuesLength } = values;
    
      for (let index = 0; index < valuesLength; index += 1) {
        array[arrayLength + index] = values[index];
      }
    
      return array.length;
    }
    --------------------------------------------------
    function filter(array, callback) {
     const result = [];
    
     const { length } = array;
    
     for (let index = 0; index < length; index += 1) {
       const value = array[index];
    
       if (callback(value, index, array)) {
         push(result, value);
       }
     }
    
     return result;
    }

獲取每個值並檢查所提供的回調函數是否返回truefalse,然後將該值添加到新創建的數組中,或者適當地丟棄它。

註意,這裡對result 數組使用push方法,而不是將值保存在傳入數組中放置的相同索引中。這樣,result就不會因為丟棄的值而有空槽。

測試

    logOperation('filter', [1, 2, 3, 4, 5], array => filter(array, value => value >= 2));

運行:

    { 
      operation: 'filter',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: [ 2, 3, 4, 5 ] 
    }

代碼部署後可能存在的BUG沒法實時知道,事後為瞭解決這些BUG,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具 [Fundebug][https://www.fundebug.com/?utm_source=xiaozhi]。

.reduce

reduce() 方法接收一個函數作為累加器,數組中的每個值(從左到右)開始縮減,最終計算為一個值reduce() 方法接受四個參數:初始值(或者上一次回調函數的返回值),當前元素值,當前索引,調用 reduce() 的數組

確切地說,如何計算該值是需要在回調中指定的。來看囈使用reduce的一個簡單的例子:對一組數字求和:

     [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].reduce((sum, number) => {
       return sum + number;
     }, 0) // -> 55

註意這裡的回調接受兩個參數:sumnumber。第一個參數總是前一個迭代返回的結果,第二個參數在遍歷中的當前數組元素。

這裡,當咱們對數組進行迭代時,sum包含到迴圈當前索引的所有數字的和因為每次迭代咱們都將數組的當前值添加到sum中。

實現

    function reduce(array, callback, initValue) {
      const { length } = array;
      
      let acc = initValue;
      let startAtIndex = 0;
    
      if (initValue === undefined) {
        acc = array[0];
        startAtIndex = 0;
      }
    
      for (let index = startAtIndex; index < length; index += 1) {
        const value = array[index];
        acc = callback(acc, value, index, array)
      }
     
      return acc;
    }

咱們創建了兩個變數accstartAtIndex,並用它們的預設值初始化它們,分別是參數initValue0

然後,檢查initValue是否是undefined。如果是,則必須將數組的第一個值設置為初值,為了不重覆計算初始元素,將startAtIndex設置為1

每次迭代,reduce方法都將回調的結果保存在累加器(acc)中,然後在下一個迭代中使用。對於第一次迭代,acc被設置為initValuearray[0]

測試

    logOperation('reduce', [1, 2, 3, 4, 5], array => reduce(array, (sum, number) => sum + number, 0));

運行:

    { operation: 'reduce',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: 15 
    }

檢索類

有什麼操作比搜索特定值更常見?這裡有一些方法可以幫助我們。

.findIndex

findIndex幫助咱們找到數組中給定值的索引。

    [1, 2, 3, 4, 5, 6, 7].findIndex(value => value === 5); // 4

findIndex方法對數組中的每個數組索引0..length-1(包括)執行一次callback函數,直到找到一個callback函數返回真實值(強製為true)的值。如果找到這樣的元素,findIndex會立即返回該元素的索引。如果回調從不返回真值,或者數組的length0,則findIndex返回-1

實現

    function findIndex(array, callback) {
     const { length } = array;
    
     for (let index = 0; index < length; index += 1) {
       const value = array[index];
    
       if (callback(value, index, array)) {
         return index;
       }
     }
    
     return -1;
    }

測試

    logOperation('findIndex', [1, 2, 3, 4, 5], array => findIndex(array, number => number === 3));

運行:

    {
      operation: 'findIndex',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: 2
    }

.find

findfindIndex的唯一區別在於,它返回的是實際值,而不是索引。實際工作中,咱們可以重用已經實現的findIndex

    [1, 2, 3, 4, 5, 6, 7].find(value => value === 5); // 5

實現

    function find(array, callback) {
     const index = findIndex(array, callback);
    
     if (index === -1) {
       return undefined;
     }
    
     return array[index];
    }

測試

    logOperation('find', [1, 2, 3, 4, 5], array => find(array, number => number === 3));

運行

    {
      operation: 'find',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: 3
    }

.indexOf

indexOf是獲取給定值索引的另一種方法。然而,這一次,咱們將實際值作為參數而不是函數傳遞。同樣,為了簡化實現,可以使用前面實現的findIndex

    [3, 2, 3].indexOf(3); // -> 0

實現

    function indexOf(array, searchedValue) {
      return findIndex(array, value => value === searchedValue)
    }

測試

    logOperation('indexOf', [1, 2, 3, 4, 5], array => indexOf(array, 3));

執行結果

    {
      operation: 'indexOf',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: 2
    }

.lastIndexOf

lastIndexOf的工作方式與indexOf相同,lastIndexOf() 方法返回指定元素在數組中的最後一個的索引,如果不存在則返回 -1

    [3, 2, 3].lastIndexOf(3); // -> 2

實現

    function lastIndexOf(array, searchedValue) {
      for (let index = array.length - 1; index > -1; index -= 1 ){
        const value = array[index];
        
        if (value === searchedValue) {
          return index;
        }
      }
      return  -1;
    }

代碼基本與findIndex類似,但是沒有執行回調,而是比較valuesearchedValue。如果比較結果為 true,則返回索引,如果找不到值,返回-1

測試

    logOperation('lastIndexOf', [1, 2, 3, 4, 5, 3], array => lastIndexOf(array, 3));

執行結果

    { 
      operation: 'lastIndexOf',
      arrayBefore: [ 1, 2, 3, 4, 5, 3 ],
      arrayAfter: [ 1, 2, 3, 4, 5, 3 ],
      mutates: false,
      result: 5 
    }

.every

every() 方法測試一個數組內的所有元素是否都能通過某個指定函數的測試,它返回一個布爾值。

    [1, 2, 3].every(value => Number.isInteger(value)); // -> true

咱們可以將every 方法看作一個等價於邏輯與的數組。

實現

    function every(array, callback){
      const { length } = array;
      
      for (let index = 0; index < length; index += 1) {
       const value = array[index];
       
        if (!callback(value, index, array)) {
          return false;
        }
      }
    
      return true;
    }

咱們為每個值執行回調。如果在任何時候返回false,則退出迴圈,整個方法返回false。如果迴圈終止而沒有進入到if語句裡面(說明條件都成立),則方法返回true

測試

    logOperation('every', [1, 2, 3, 4, 5], array => every(array, number => Number.isInteger(number)));

執行結果

    {
      operation: 'every',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: true 
    }

.some

some 方法與 every 剛好相反,即只要其中一個為true 就會返回true。與every 方法類似,咱們可以將some 方法看作一個等價於邏輯或數組。

    [1, 2, 3, 4, 5].some(number => number === 5); // -> true

實現

    function some(array, callback) {
     const { length } = array;
    
     for (let index = 0; index < length; index += 1) {
       const value = array[index];
    
       if (callback(value, index, array)) {
         return true;
       }
     }
    
     return false;
    }

咱們為每個值執行回調。如果在任何時候返回true,則退出迴圈,整個方法返回true。如果迴圈終止而沒有進入到if語句裡面(說明條件都不成立),則方法返回false

測試

    logOperation('some', [1, 2, 3, 4, 5], array => some(array, number => number === 5));

執行結果

    {
      operation: 'some',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: true
    }

.includes

includes方法的工作方式類似於 some 方法,但是includes不用回調,而是提供一個參數值來比較元素。

    [1, 2, 3].includes(3); // -> true

實現

    function includes(array, searchedValue){
      return some(array, value => value === searchedValue)
    }

測試

    logOperation('includes', [1, 2, 3, 4, 5], array => includes(array, 5));

執行結果

    {
      operation: 'includes',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: true
    }

拼接、附加和反轉數組

.concat

concat() 方法用於合併兩個或多個數組,此方法不會更改現有數組,而是返回一個新數組。

    [1, 2, 3].concat([4, 5], 6, [7, 8]) // -> [1, 2, 3, 4, 5, 6, 7, 8]

實現

    function concat(array, ...values) {
      const result = [...array];
      const { length } = values;
    
      for (let index = 0; index < length; index += 1) {
        const value = values[index];
        
        if (Array.isArray(value)) {
          push(result, ...value);
        } else {
          push(result, value);
        }
      }
    
      return result;
    }

concat將數組作為第一個參數,並將未指定個數的值作為第二個參數,這些值可以是數組,也可以是其他類型的值。

首先,通過複製傳入的數組創建 result 數組。然後,遍歷 values ,檢查該值是否是數組。如果是,則使用push函數將其值附加到結果數組中。

push(result, value) 只會向數組追加為一個元素。相反,通過使用展開操作符push(result,…value) 將數組的所有值附加到result 數組中。在某種程度上,咱們把數組扁平了一層。

測試

    logOperation('concat', [1, 2, 3, 4, 5], array => concat(array, 1, 2, [3, 4]));

執行結果

    { 
     operation: 'concat',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: [ 1, 2, 3, 4, 5, 1, 2, 3, 4 ] 
    }

.join

join() 方法用於把數組中的所有元素放入一個字元串,元素是通過指定的分隔符進行分隔的。

    ['Brian', 'Matt', 'Kate'].join(', ') // -> Brian, Matt, Kate

實現

    function join(array, joinWith) {
      return reduce(
        array,
        (result, current, index) => {
          if (index === 0) {
            return current;
          }
          
          return `${result}${joinWith}${current}`;
        },
        ''
      )
    }

reduce的回調是神奇之處:reduce遍歷所提供的數組並將結果字元串拼接在一起,在數組的值之間放置所需的分隔符(作為joinWith傳遞)。

array[0]值需要一些特殊的處理,因為此時result是一個空字元串,而且咱們也不希望分隔符(joinWith)位於第一個元素前面。

測試

    logOperation('join', [1, 2, 3, 4, 5], array => join(array, ', '));

執行結果

    {
      operation: 'join',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: '1, 2, 3, 4, 5'
    }

.reverse

reverse() 方法將數組中元素的位置顛倒,並返回該數組,該方法會改變原數組。

實現

    function reverse(array) {
      const result = []
      const lastIndex = array.length - 1;
    
      for (let index = lastIndex; index > -1; index -= 1) {
        const value = array[index];
        result[lastIndex - index ] = value
      }
      return result;
    }

其思路很簡單:首先,定義一個空數組,並將數組的最後一個索引保存為變數(lastIndex)。接著反過來遍曆數組,將每個值保存在結果result 中的(lastIndex - index)位置,然後返回result數組。

測試

    logOperation('reverse', [1, 2, 3, 4, 5], array => reverse(array));

執行結果

    {
      operation: 'reverse',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: [ 5, 4, 3, 2, 1 ]
    }

添加、刪除和追加值

.shift

shift() 方法從數組中刪除第一個元素,並返回該元素的值,此方法更改數組的長度。

    [1, 2, 3].shift(); // -> 1

實現

    function shift(array) {
      const { length } = array;
      const firstValue = array[0];
    
      for (let index = 1; index > length; index += 1) {
        const value = array[index];
        array[index - 1] = value;
      }
    
      array.length = length - 1;
    
      return firstValue;
    }

首先保存數組的原始長度及其初始值,然後遍曆數組並將每個值向下移動一個索引。完成遍歷後,更新數組的長度並返回初始值。

測試

    logOperation('shift', [1, 2, 3, 4, 5], array => shift(array));

執行結果

    {
      operation: 'shift',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 2, 3, 4, 5 ],
      mutates: true,
      result: 1
    }

.unshift

unshift() 方法將一個或多個元素添加到數組的開頭,並返回該數組的新長度(該方法修改原有數組)。

    [2, 3, 4].unshift(1); // -> [1, 2, 3, 4]

實現

    function unshift(array, ...values) {
      const mergedArrays = concat(values, ...array);
      const { length: mergedArraysLength } = mergedArrays;
    
      for (let index = 0; index < mergedArraysLength; index += 1) {
        const value = mergedArrays[index];
        array[index] = value;
      }
    
      return array.length;
    }

首先將需要加入數組(作為參數傳遞的單個值)和數組拼接起來。這裡需要註意的是values 放在第一位的,也就是放置在原始數組的前面。

然後保存這個新數組的長度並遍歷它,將它的值保存在原始數組中,並覆蓋開始時的值。

測試

logOperation('unshift', [1, 2, 3, 4, 5], array => unshift(array, 0));

執行結果

    {
      operation: 'unshift',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 0, 1, 2, 3, 4, 5 ],
      mutates: true,
      result: 6
    }

.slice

    slice() 

方法返回一個新的數組對象,這一對象是一個由 beginend 決定的原數組的淺拷貝(包括 begin,不包括end)原始數組不會被改變。

slice 會提取原數組中索引從 beginend 的所有元素(包含 begin,但不包含 end)。

    [1, 2, 3, 4, 5, 6, 7].slice(3, 6); // -> [4, 5, 6]

實現 (簡單實現)

    function slice(array, startIndex = 0, endIndex = array.length) {
     const result = [];
    
     for (let index = startIndex; index < endIndex; index += 1) {
       const value = array[index];
    
       if (index < array.length) {
         push(result, value);
       }
     }
    
     return result;
    }

咱們遍曆數組從startIndexendIndex,並將每個值放入result。這裡使用了這裡的預設參數,這樣當沒有傳遞參數時,slice方法只創建數組的副本。

註意:if語句確保只在原始數組中存在給定索引下的值時才加入 result 中。

測試

    logOperation('slice', [1, 2, 3, 4, 5], array => slice(array, 1, 3));

執行結果

    {
      operation: 'slice',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: [ 2, 3 ]
    }

.splice

splice() 方法通過刪除或替換現有元素或者原地添加新的元素來修改數組,並以數組形式返回被修改的內容。此方法會改變原數組。

首先,指定起始索引,然後指定要刪除多少個值,其餘的參數是要插入的值。

    const arr = [1, 2, 3, 4, 5];
    // 從位置0開始,刪除2個元素後插入 3, 4, 5
    arr.splice(0, 2, 3, 4, 5);
    
    arr // -> [3, 4, 5, 3, 4, 5]

實現

    function splice( array, insertAtIndex, removeNumberOfElements, ...values) {
      const firstPart = slice(array, 0, insertAtIndex);
      const secondPart = slice(array, insertAtIndex + removeNumberOfElements);
    
      const removedElements = slice(
        array,
        insertAtIndex,
        insertAtIndex + removeNumberOfElements
      );
    
      const joinedParts = firstPart.concat(values, secondPart);
      const { length: joinedPartsLength } = joinedParts;
    
      for (let index = 0; index < joinedPartsLength; index += 1) {
        array[index] = joinedParts[index];
      }
    
      array.length = joinedPartsLength;
    
      return removedElements;
    }

其思路是在insertAtIndexinsertAtIndex + removeNumberOfElements上進行兩次切割。這樣,將原始數組切成三段。第一部分(firstPart)和第三部分(secondPart)加個插入的元素組成為最後數組的內容。

測試

    logOperation('splice', [1, 2, 3, 4, 5], array => splice(array, 1, 3));

執行結果

    {
      operation: 'splice',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 5 ],
      mutates: true,
      result: [ 2, 3, 4 ]
    }

.pop

pop()方法從數組中刪除最後一個元素,並返回該元素的值。此方法更改數組的長度。

實現

    function pop(array) {
     const value = array[array.length - 1];
    
     array.length = array.length - 1;
    
     return value;
    }

首先,將數組的最後一個值保存在一個變數中。然後只需將數組的長度減少1,從而刪除最後一個值。

測試

    logOperation('pop', [1, 2, 3, 4, 5], array => pop(array));

執行結果

    {
      operation: 'pop',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4 ],
      mutates: true,
      result: 5
    }

.push

push() 方法將一個或多個元素添加到數組的末尾,並返回該數組的新長度。

    [1, 2, 3, 4].push(5); // -> [1, 2, 3, 4, 5]

實現

    function push(array, ...values) {
      const { length: arrayLength } = array;
      const { length: valuesLength } = values;
    
      for (let index = 0; index < valuesLength; index += 1) {
        array[arrayLength + index] = values[index];
      }
    
      return array.length;
    }

首先,我們保存原始數組的長度,以及在它們各自的變數中要添加的值。然後,遍歷提供的值並將它們添加到原始數組中。

測試

    logOperation('push', [1, 2, 3, 4, 5], array => push(array, 6, 7));

執行結果

    {
      operation: 'push',
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [
        1, 2, 3, 4,5, 6, 7
      ],
      mutates: true,
      result: 7
    }

.fill

當咱們想用一個占位符值填充一個空數組時,可以使用fill方法。如果想創建一個指定數量的null元素數組,可以這樣做:

    [...Array(5)].fill(null) // -> [null, null, null, null, null]

實現

    function fill(array, value, startIndex = 0, endIndex = array.length) {
     for (let index = startIndex; index < endIndex; index += 1) {
       array[index] = value;
     }
    
     return array;
    }

fill方法真正做的是替換指定索引範圍內的數組的值。如果沒有提供範圍,該方法將替換所有數組的值。

測試

    logOperation("fill", [...new Array(5)], array => fill(array, 0));

執行結果

    {
      operation: 'fill',
      arrayBefore: [ undefined, undefined, undefined, undefined, undefined ],
      arrayAfter: [ 0, 0, 0, 0, 0 ],
      mutates: true,
      result: [ 0, 0, 0, 0, 0 ]
    }

扁平類

有時咱們的數組會變嵌套兩到三層,咱們想要將它們扁,也就是減少嵌套的程度。例如,想將所有值都放到頂層。為咱們提供幫助有兩個新特性:flatflatMap 方法。

.flat

flat方法通過可指定深度值來減少嵌套的深度。

    [1, 2, 3, [4, 5, [6, 7, [8]]]].flat(1); // -> [1, 2, 3, 4, 5, [6, 7, [8]]]

因為展開的深度值是1,所以只有第一級數組是被扁平,其餘的保持不變。

    [1, 2, 3, [4, 5]].flat(1) // -> [1, 2, 3, 4, 5]

實現

    function flat(array, depth = 0) {
     if (depth < 1 || !Array.isArray(array)) {
       return array;
     }
    
     return reduce(
       array,
       (result, current) => {
         return concat(result, flat(current, depth - 1));
       },
       [],
     );
    }

首先,我們檢查depth參數是否小於1。如果是,那就意味著沒有什麼要扁平的,咱們應該簡單地返回數組。

其次,咱們檢查數組參數是否屬於數組類型,因為如果它不是,那麼扁化就沒有意義了,所以只返回這個參數。

咱們們使用了之前實現的reduce函數。從一個空數組開始,然後取數組的每個值並將其扁平。

註意,我們調用帶有(depth - 1)flat函數。每次調用時,都遞減depth參數,以免造成無限迴圈。扁平化完成後,將返回值來回加到result數組中。

測試

    logOperation('flat', [1, 2, 3, [4, 5, [6]]], array => flat(array, 2));

執行結果

    {
      operation: 'flat',
      arrayBefore: [ 1, 2, 3, [ 4, 5, [Array] ] ],
      arrayAfter: [ 1, 2, 3, [ 4, 5, [Array] ] ],
      mutates: false,
      result: [ 1, 2, 3, 4, 5, 6 ]
    }

.flatMap

flatMap() 方法首先使用映射函數映射每個元素,然後將結果壓縮成一個新數組。它與 map 和 深度值1的 flat 幾乎相同,但 flatMap 通常在合併成一種方法的效率稍微高一些。

在上面的map方法中,對於每個值,只返回一個值。這樣,一個包含三個元素的數組在映射之後仍然有三個元素。使用flatMap,在提供的回調函數中,可以返回一個數組,這個數組稍後將被扁平。

    [1, 2, 3].flatMap(value => [value, value, value]); // [1, 1, 1, 2, 2, 2, 3, 3, 3]

每個返回的數組都是扁平的,我們得到的不是一個嵌套了三個數組的數組,而是一個包含9個元素的數組。

實現

    function flatMap(array, callback) {
     return flat(map(array, callback), 1);
    }

首先使用map,然後將數組的結果數組扁平化一層。

測試

    logOperation('flatMap', [1, 2, 3], array => flatMap(array, number => [number, number]));

執行結果

    {
      operation: 'flatMap',
      arrayBefore: [ 1, 2, 3 ],
      arrayAfter: [ 1, 2, 3 ],
      mutates: false,
      result: [ 1, 1, 2, 2, 3, 3 ]
    }

generator 類

最後三種方法的特殊之處在於它們返回生成器的方式。如果你不熟悉生成器,請跳過它們,因為你可能不會很快使用它們。

.values

values方法返回一個生成器,該生成器生成數組的值。

    const valuesGenerator = values([1, 2, 3, 4, 5]);
    
    valuesGenerator.next(); // { value: 1, done: false }

實現

    function values(array) {
     const { length } = array;
    
     function* createGenerator() {
       for (let index = 0; index < length; index += 1) {
         const value = array[index];
         yield value;
       }
     }
    
     return createGenerator();
    }

首先,咱們定義createGenerator函數。在其中,咱們遍曆數組並生成每個值。

.keys

keys方法返回一個生成器,該生成器生成數組的索引。

    const keysGenerator = keys([1, 2, 3, 4, 5]);
    
    keysGenerator.next(); // { value: 0, done: false }

實現

    function keys(array) {
     function* createGenerator() {
       const { length } = array;
    
       for (let index = 0; index < length; index += 1) {
         yield index;
       }
     }
    
     return createGenerator();
    }

實現完全相同,但這一次,生成的是索引,而不是值。

.entries

entry方法返回生成鍵值對的生成器。

    const entriesGenerator = entries([1, 2, 3, 4, 5]);
    
    entriesGenerator.next(); // { value: [0, 1], done: false }

實現

    function entries(array) {
     const { length } = array;
    
     function* createGenerator() {
       for (let index = 0; index < length; index += 1) {
         const value = array[index];
         yield [index, value];
       }
     }
    
     return createGenerator();
    }

同樣的實現,但現在咱們將索引和值結合起來,併在數組中生成它們。

總結

高效使用數組的方法是成為一名優秀開發人員的基礎。瞭解他們內部工作的複雜性是我所知道的最好的方法。

代碼部署後可能存在的BUG沒法實時知道,事後為瞭解決這些BUG,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具 [Fundebug][https://www.fundebug.com/?utm_source=xiaozhi]。

原文:https://dev.to/bnevilleoneill/understand-array-methods-by-implementing-them-all-of-them-iha

關於Fundebug

Fundebug專註於JavaScript、微信小程式、微信小游戲、支付寶小程式、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了20億+錯誤事件,付費客戶有陽光保險、核桃編程、荔枝FM、掌門1對1、微脈、青團社等眾多品牌企業。歡迎大家免費試用


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 新建plugin.js,文件內容如下 2 在main.js中引入 至此,方式屬性已經全局引入,在vue頁面可以使用 ...
  • HTML是一種描述網頁的語言。全稱是Hyper Text Markup Language(超文本標記語言),是一種標記語言,由許多的標記標簽組成。 一 HTML標簽 1,簡介 HTML語言是由許多標簽組成,HTML 標簽是由尖括弧包圍的關鍵詞,比如 <html>。 HTML標簽都是小寫的。 HTML ...
  • 轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。原文出處:https://blog.bitsrc.io/pure-html-widgets-for-your-web-application-c9015563af7a 在我們以往看到的頁面效果中,很多效果是需要 ...
  • 0920自我總結 關於hover與after,before已及first letter,first line的聯用 一.寫法 元素:hover::after{樣式} 元素:hover::before{樣式} 二.使用 元素:hover::after{樣式}與元素:after{content:'插入的 ...
  • 效果 效果圖如下 ​ ​ 實現思路 dom結構 按照實現思路,我們需要如下的dom結構 <div class="container"> <div class="cube-wrap"> <div class="cube"> <div class="front">front</div> <div cla ...
  • 最近,遇到複雜h5頁面開發,為了優化H5首屏載入速度,想到使用按需載入的方式,減少首次載入的JavaScript文件體積,於是將處理過程在這裡記錄一下,涉及到的主要是以下三點: 使用Webpack如何做按需載入 filename和chunkFilename的區別 如何命名chunk的名稱(webpa ...
  • 1、first-child first-child表示選擇列表中的第一個標簽。例如:li:first-child{background:#fff} 2、last-child last-child表示選擇列表中的最後一個標簽,例如:li:last-child{background:#fff} 3、nt ...
  • 1、js 獲取當前日期星期幾 getWeek(time) { let weekDay = ["周日","周一", "周二", "周三", "周四", "周五", "周六"]; let myDate = new Date(Date.parse(time)); return weekDay[myDate ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...