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ù)組中的每個值(從左到右)開始縮減胞谭,最終計算為一個值垃杖。reduce() 方法接受四個參數(shù):初始值(或者上一次回調(diào)函數(shù)的返回值),當(dāng)前元素值丈屹,當(dāng)前索引调俘,調(diào)用 reduce() 的數(shù)組

確切地說旺垒,如何計算該值是需要在回調(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ù)計算初始元素,將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元素數(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)該簡單地返回數(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累計處理了20億+錯誤事件,付費(fèi)客戶有陽光保險诺苹、核桃編程咕晋、荔枝FM、掌門1對1收奔、微脈掌呜、青團(tuán)社等眾多品牌企業(yè)。歡迎大家免費(fèi)試用坪哄!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末质蕉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子翩肌,更是在濱河造成了極大的恐慌模暗,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件念祭,死亡現(xiàn)場離奇詭異兑宇,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)粱坤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門隶糕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人比规,你說我怎么就攤上這事若厚。” “怎么了蜒什?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵测秸,是天一觀的道長。 經(jīng)常有香客問我灾常,道長霎冯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任钞瀑,我火速辦了婚禮沈撞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雕什。我一直安慰自己缠俺,他們只是感情好显晶,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著壹士,像睡著了一般磷雇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上躏救,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天唯笙,我揣著相機(jī)與錄音,去河邊找鬼盒使。 笑死崩掘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的少办。 我是一名探鬼主播苞慢,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼英妓!你這毒婦竟也來了枉疼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤鞋拟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后惹资,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贺纲,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年褪测,在試婚紗的時候發(fā)現(xiàn)自己被綠了猴誊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡侮措,死狀恐怖懈叹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情分扎,我是刑警寧澤澄成,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站畏吓,受9級特大地震影響墨状,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜菲饼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一肾砂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宏悦,春花似錦镐确、人聲如沸包吝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诗越。三九已至,卻和暖如春臼氨,著一層夾襖步出監(jiān)牢的瞬間掺喻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工储矩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留感耙,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓持隧,卻偏偏與公主長得像即硼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子屡拨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容