25個JavaScript數(shù)組方法代碼示例

摘要: 通過代碼掌握數(shù)組方法。

Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。

要在給定數(shù)組上使用方法,只需要通過[].方法名即可,這些方法都定義在 Array.prototype 對象上。在這里,咱們先不使用這些相,反,咱們將從簡單的方法開始定義自己的版本,并在這些版本的基礎(chǔ)上進(jìn)行構(gòu)建。

沒有比把東西拆開再重新組裝起來更好的學(xué)習(xí)方法了。注意,當(dāng)咱們的實(shí)現(xiàn)自己的方法時,不要覆蓋現(xiàn)有的方法,因?yàn)橛械膸煨枰鼈?,并且這樣也方便比較咱們自己的方法與原始方法的差異。

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

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

最好這樣命名:

    function map(array) {
     // implementation
    }

咱們也可以通過使用class關(guān)鍵字并擴(kuò)展Array構(gòu)造函數(shù)來實(shí)現(xiàn)咱們的方法,如下所示:

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

唯一的區(qū)別是,我們不使用數(shù)組參數(shù),而是使用this關(guān)鍵字。

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

有了這個,咱們先從實(shí)現(xiàn)最簡單的方法 forEach 開始!

集合類

.forEach

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

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

實(shí)現(xiàn)

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

咱們遍歷數(shù)組并為每個元素執(zhí)行回調(diào)。這里需要注意的一點(diǎn)是,該方法沒有返回什么,所以默認(rèn)返回undefined。

方法漣

使用數(shù)組方法的好處是可以將操作鏈接在一起??紤]以下代碼:

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

這種方式,咱們就不必將map的執(zhí)行結(jié)果保存到變量中,代碼會更簡潔。

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

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

幫助函數(shù) (打印信息)

接著實(shí)現(xiàn)一個簡單的函數(shù),它能更好地解釋每個方法的功能:接受什么作為輸入,返回什么,以及它是否對數(shù)組進(jìn)行了修改。

    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 方法用來判斷是否更改了原數(shù)組,如果有修改剛返回 true,否則返回 false。當(dāng)然大伙有好的想法可以在評論提出呦。

    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來測試咱們前面自己實(shí)現(xiàn)的 forEach方法。

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

打印結(jié)果:

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

.map

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

實(shí)現(xiàn)

    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;
    }

提供給方法的回調(diào)函數(shù)接受舊值作為參數(shù),并返回一個新值,然后將其保存在新數(shù)組中的相同索引下,這里用變量 result 表示。

這里需要注意的是,咱們返回了一個新的數(shù)組,不修改舊的。

測試

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

打印結(jié)果:

    { 
      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 過濾回調(diào)返回為false的值,每個值都保存在一個新的數(shù)組中,然后返回。

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

實(shí)現(xiàn)

    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;
    }

獲取每個值并檢查所提供的回調(diào)函數(shù)是否返回truefalse,然后將該值添加到新創(chuàng)建的數(shù)組中,或者適當(dāng)?shù)貋G棄它。

注意,這里對result 數(shù)組使用push方法,而不是將值保存在傳入數(shù)組中放置的相同索引中。這樣,result就不會因?yàn)閬G棄的值而有空槽。

測試

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

運(yùn)行:

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

代碼部署后可能存在的BUG沒法實(shí)時知道,事后為了解決這些BUG,花了大量的時間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個好用的BUG監(jiān)控工具 [Fundebug][https://www.fundebug.com/?utm_source=xiaozhi]。

.reduce

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

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

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

注意這里的回調(diào)接受兩個參數(shù):sumnumber。第一個參數(shù)總是前一個迭代返回的結(jié)果,第二個參數(shù)在遍歷中的當(dāng)前數(shù)組元素。

這里,當(dāng)咱們對數(shù)組進(jìn)行迭代時,sum包含到循環(huán)當(dāng)前索引的所有數(shù)字的和因?yàn)槊看蔚蹅兌紝?shù)組的當(dāng)前值添加到sum中。

實(shí)現(xiàn)

    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;
    }

咱們創(chuàng)建了兩個變量accstartAtIndex,并用它們的默認(rèn)值初始化它們,分別是參數(shù)initValue0。

然后,檢查initValue是否是undefined。如果是,則必須將數(shù)組的第一個值設(shè)置為初值,為了不重復(fù)計(jì)算初始元素,將startAtIndex設(shè)置為1。

每次迭代,reduce方法都將回調(diào)的結(jié)果保存在累加器(acc)中,然后在下一個迭代中使用。對于第一次迭代,acc被設(shè)置為initValuearray[0]。

測試

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

運(yùn)行:

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

檢索類

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

.findIndex

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

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

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

實(shí)現(xiàn)

    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));

運(yùn)行:

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

.find

findfindIndex的唯一區(qū)別在于,它返回的是實(shí)際值,而不是索引。實(shí)際工作中,咱們可以重用已經(jīng)實(shí)現(xiàn)的findIndex

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

實(shí)現(xiàn)

    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));

運(yùn)行

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

.indexOf

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

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

實(shí)現(xiàn)

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

測試

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

執(zhí)行結(jié)果

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

.lastIndexOf

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

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

實(shí)現(xiàn)

    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類似,但是沒有執(zhí)行回調(diào),而是比較valuesearchedValue。如果比較結(jié)果為 true,則返回索引,如果找不到值,返回-1。

測試

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

執(zhí)行結(jié)果

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

.every

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

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

咱們可以將every 方法看作一個等價于邏輯與的數(shù)組。

實(shí)現(xiàn)

    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;
    }

咱們?yōu)槊總€值執(zhí)行回調(diào)。如果在任何時候返回false,則退出循環(huán),整個方法返回false。如果循環(huán)終止而沒有進(jìn)入到if語句里面(說明條件都成立),則方法返回true。

測試

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

執(zhí)行結(jié)果

    {
      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 方法看作一個等價于邏輯或數(shù)組。

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

實(shí)現(xiàn)

    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;
    }

咱們?yōu)槊總€值執(zhí)行回調(diào)。如果在任何時候返回true,則退出循環(huán),整個方法返回true。如果循環(huán)終止而沒有進(jìn)入到if語句里面(說明條件都不成立),則方法返回false。

測試

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

執(zhí)行結(jié)果

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

.includes

includes方法的工作方式類似于 some 方法,但是includes不用回調(diào),而是提供一個參數(shù)值來比較元素。

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

實(shí)現(xiàn)

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

測試

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

執(zhí)行結(jié)果

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

拼接、附加和反轉(zhuǎn)數(shù)組

.concat

concat() 方法用于合并兩個或多個數(shù)組,此方法不會更改現(xiàn)有數(shù)組,而是返回一個新數(shù)組。

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

實(shí)現(xiàn)

    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將數(shù)組作為第一個參數(shù),并將未指定個數(shù)的值作為第二個參數(shù),這些值可以是數(shù)組,也可以是其他類型的值。

首先,通過復(fù)制傳入的數(shù)組創(chuàng)建 result 數(shù)組。然后,遍歷 values ,檢查該值是否是數(shù)組。如果是,則使用push函數(shù)將其值附加到結(jié)果數(shù)組中。

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

測試

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

執(zhí)行結(jié)果

    { 
     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() 方法用于把數(shù)組中的所有元素放入一個字符串,元素是通過指定的分隔符進(jìn)行分隔的。

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

實(shí)現(xiàn)

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

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

array[0]值需要一些特殊的處理,因?yàn)榇藭rresult是一個空字符串,而且咱們也不希望分隔符(joinWith)位于第一個元素前面。

測試

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

執(zhí)行結(jié)果

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

.reverse

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

實(shí)現(xiàn)

    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;
    }

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

測試

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

執(zhí)行結(jié)果

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

添加、刪除和追加值

.shift

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

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

實(shí)現(xiàn)

    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;
    }

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

測試

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

執(zhí)行結(jié)果

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

.unshift

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

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

實(shí)現(xiàn)

    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;
    }

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

然后保存這個新數(shù)組的長度并遍歷它,將它的值保存在原始數(shù)組中,并覆蓋開始時的值。

測試

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

執(zhí)行結(jié)果

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

.slice

    slice() 

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

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

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

實(shí)現(xiàn) (簡單實(shí)現(xiàn))

    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;
    }

咱們遍歷數(shù)組從startIndexendIndex,并將每個值放入result。這里使用了這里的默認(rèn)參數(shù),這樣當(dāng)沒有傳遞參數(shù)時,slice方法只創(chuàng)建數(shù)組的副本。

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

測試

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

執(zhí)行結(jié)果

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

.splice

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

首先,指定起始索引,然后指定要刪除多少個值,其余的參數(shù)是要插入的值。

    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]

實(shí)現(xiàn)

    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上進(jìn)行兩次切割。這樣,將原始數(shù)組切成三段。第一部分(firstPart)和第三部分(secondPart)加個插入的元素組成為最后數(shù)組的內(nèi)容。

測試

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

執(zhí)行結(jié)果

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

.pop

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

實(shí)現(xiàn)

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

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

測試

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

執(zhí)行結(jié)果

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

.push

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

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

實(shí)現(xiàn)

    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;
    }

首先,我們保存原始數(shù)組的長度,以及在它們各自的變量中要添加的值。然后,遍歷提供的值并將它們添加到原始數(shù)組中。

測試

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

執(zhí)行結(jié)果

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

.fill

當(dāng)咱們想用一個占位符值填充一個空數(shù)組時,可以使用fill方法。如果想創(chuàng)建一個指定數(shù)量的null元素?cái)?shù)組,可以這樣做:

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

實(shí)現(xiàn)

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

fill方法真正做的是替換指定索引范圍內(nèi)的數(shù)組的值。如果沒有提供范圍,該方法將替換所有數(shù)組的值。

測試

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

執(zhí)行結(jié)果

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

扁平類

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

.flat

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

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

因?yàn)檎归_的深度值是1,所以只有第一級數(shù)組是被扁平,其余的保持不變。

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

實(shí)現(xiàn)

    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參數(shù)是否小于1。如果是,那就意味著沒有什么要扁平的,咱們應(yīng)該簡單地返回?cái)?shù)組。

其次,咱們檢查數(shù)組參數(shù)是否屬于數(shù)組類型,因?yàn)槿绻皇?,那么扁化就沒有意義了,所以只返回這個參數(shù)。

咱們們使用了之前實(shí)現(xiàn)的reduce函數(shù)。從一個空數(shù)組開始,然后取數(shù)組的每個值并將其扁平。

注意,我們調(diào)用帶有(depth - 1)flat函數(shù)。每次調(diào)用時,都遞減depth參數(shù),以免造成無限循環(huán)。扁平化完成后,將返回值來回加到result數(shù)組中。

測試

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

執(zhí)行結(jié)果

    {
      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() 方法首先使用映射函數(shù)映射每個元素,然后將結(jié)果壓縮成一個新數(shù)組。它與 map 和 深度值1的 flat 幾乎相同,但 flatMap 通常在合并成一種方法的效率稍微高一些。

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

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

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

實(shí)現(xiàn)

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

首先使用map,然后將數(shù)組的結(jié)果數(shù)組扁平化一層。

測試

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

執(zhí)行結(jié)果

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

generator 類

最后三種方法的特殊之處在于它們返回生成器的方式。如果你不熟悉生成器,請?zhí)^它們,因?yàn)槟憧赡懿粫芸焓褂盟鼈儭?/p>

.values

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

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

實(shí)現(xiàn)

    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函數(shù)。在其中,咱們遍歷數(shù)組并生成每個值。

.keys

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

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

實(shí)現(xiàn)

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

實(shí)現(xiàn)完全相同,但這一次,生成的是索引,而不是值。

.entries

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

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

實(shí)現(xiàn)

    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();
    }

同樣的實(shí)現(xiàn),但現(xiàn)在咱們將索引和值結(jié)合起來,并在數(shù)組中生成它們。

總結(jié)

高效使用數(shù)組的方法是成為一名優(yōu)秀開發(fā)人員的基礎(chǔ)。了解他們內(nèi)部工作的復(fù)雜性是我所知道的最好的方法。

代碼部署后可能存在的BUG沒法實(shí)時知道,事后為了解決這些BUG,花了大量的時間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個好用的BUG監(jiān)控工具 [Fundebug][https://www.fundebug.com/?utm_source=xiaozhi]。

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

關(guān)于Fundebug

Fundebug專注于JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java線上應(yīng)用實(shí)時BUG監(jiān)控。 自從2016年雙十一正式上線,F(xiàn)undebug累計(jì)處理了20億+錯誤事件,付費(fèi)客戶有陽光保險、核桃編程、荔枝FM、掌門1對1、微脈、青團(tuán)社等眾多品牌企業(yè)。歡迎大家免費(fèi)試用

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容