30秒就能理解的 Javascript 代碼片斷 --- Array篇

以前翻譯過一篇文章,《我喜歡的5個編程技巧》,裏面的一個技巧是借鑑一個網站的代碼片斷,好奇的小手點下連接後,發現是一個有 47000 多star的倉庫,30-seconds-of-codejavascript

倉庫的名字就讓我很驚訝,30秒就能理解一段代碼,有點難以想象。看了每一個方法實現的代碼都不長,很簡短,最複雜的也不過是四、5行代碼,的確沒有標題黨的嫌疑,滿滿的幹活。css

處理的類型還很豐富,有Array、Browser、Date、Function、Math、Node、Object、String、Type、Utility。要了解更多,戳這裏前端

這就是一個工具函數庫呀。java

此時讓我想到了 lodash.jsunderscore.js,用過這兩個函數式編程的庫,對提供的方法確定不陌生。它們主要是以函數做爲主要載體的編程方式,用函數去拆解、抽象的表達式,每一個函數封裝特定的功能,做用域侷限在函數內部,造成閉包,不對外界產生反作用。react

相信也有不少人閱讀過它們的源碼,每一個函數很簡短,考慮到兼容性,基本都用原生的方式實現,不會調用一些規範中最新推出的方法。若是可以精讀它們,對本身的編程能力會有更高的提高,可以掌握不少的技巧。有時你可能只是想快速的瞭解一個方法大體的實現原理,但要去看源碼的話,仍是會有一些門檻。git

而這個30秒就能理解的代碼片斷,摒棄了許多沒必要要的代碼,只實現了最核心的部分,不像 lodash.jsunderscore.js 那樣,考慮參數邊界值問題,例如,參數的類型是否符合預期等。默認狀況下,都是按照傳遞符合預期的參數處理。github

若是要把這個庫用在本身的項目中,沒有對參數的判斷是很是糟糕的一件事。但不想引 lodash.jsunderscore.js 這樣大的庫文件,想本身實現一個簡潔的方法快速使用,那麼這個庫會對你實現本身的方法具備指導意義。不考慮兼容問題的話,你能夠直接拷貝這個庫的代碼片斷,加上對參數邊界的處理就直接能用。編程

再有一點,這個庫之因此簡短,可以讓你在30秒就理解,主要是能用規範提供的最新方法就用,再也不很費勁的本身實現一套,全都調用了原生提供的方法,包括 ES6 的方法。每一個方法都是獨立的,可獨立測試,獨立運行,和其餘的方法互不牽扯,極大的下降了閱讀時找各類方法會被打斷思路的煩惱。segmentfault

固然,若是你想閱讀 lodash.jsunderscore.js 的源碼,先閱讀這個庫會頗有幫助,它排除了許多沒必要要的干擾讓你很清晰很明確的get到核心的實現方式。數組

以前也有人翻譯過,但都很早,大約2年前了,做者最新最近更新的方法都沒有。並且倉庫中不止提供 javascript 的方法,還有 css react 的簡短代碼,還有其餘語言的。基於本身學習的目的,同時也讓更多人掌握這些方法的實現方式,決定翻譯成中文。

倉庫地址:https://github.com/WYseven/30-seconds-of-code。感興趣的,能夠給個 star 哦!

目前已完成數組方法的翻譯,點擊查看 https://wyseven.github.io/30-seconds-of-code/。其餘方法也在持續的更新中。。。

我不建議你悶着頭一口氣讀完,而後頭昏眼花的不知道本身看了什麼。而是建議你在閒暇之餘,工做空隙,斷斷續續,一天看幾個就夠了,權當作工做累了休憩時當作消遣來看。

由於篇幅的緣由,如下隨機選擇了10個方法,你看簡單不簡單。

chunk
deepFlatten
flatten
initialize2DArray
union
mapObject
pull
reducedFilter
xProd

chunk

將數組分塊成指定大小的較小數組。

使用 array.from() 建立一個新的數組,該數組的長度就是將要生成的塊(chunk)的個數。
使用 array.prototype.slice() 將新數組的每一個元素映射爲一個長度爲 size 的塊(chunk)。
若是原始數組不能被平均分割,那麼最後的塊(chunk)將包含剩餘的元素。

const chunk = (arr, size) =>
    Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>
    arr.slice(i * size, i * size + size)
);
chunk([1, 2, 3, 4, 5], 2); // [[1,2],[3,4],[5]]

deepFlatten

深度平鋪一個數組。

使用遞歸。
使用 array. prototype.concat() 和空數組( [] ),結合 spread 操做符('...')將數組平鋪。
遞歸平鋪數組中的每一個元素。

const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));
deepFlatten([1, [2], [[3], 4], 5]); // [1,2,3,4,5]

flatten

將數組展平到指定的深度。

使用遞歸,爲每一個深度級別 depth 遞減 1。 使用 Array.prototype.reduce()Array.prototype.concat() 來合併元素或數組。 基本狀況下,depth 等於 1 中止遞歸。 省略第二個參數,depth 只能平鋪到 1 層(單層平鋪) 的深度。

const flatten = (arr, depth = 1) =>
    arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v), []);
flatten([1, [2], 3, 4]); // [1, 2, 3, 4]
flatten([1, [2, [3, [4, 5], 6], 7], 8], 2); // [1, 2, 3, [4, 5], 6, 7, 8]

initialize2DArray

初始化一個給定行數和列數,以及值的二維數組。

使用 array.prototype.map() 生成 h 行,其中每一行都是長度爲 w 的新數組。若是沒有提供值 val,則默認爲 null

const initialize2DArray = (w, h, val = null) =>
    Array.from({ length: h }).map(() => Array.from({ length: w }).fill(val));
initialize2DArray(2, 2, 0); // [[0,0], [0,0]]

union

返回兩個數組的並集,相同的元素只出現一次。

基於 ab 建立一個 Set 對象,返回轉換後的數組。

const union = (a, b) => Array.from(new Set([...a, ...b]));
union([1, 2, 3], [4, 3, 2]); // [1,2,3,4]

mapObject

使用一個函數將數組的值映射到對象,在鍵值對中,原始值做爲鍵,映射值做爲值。

使用一個匿名的內部函數做用域來聲明一個 undefined 的內存空間,使用閉包來存儲返回值。 使用一個新的 Array 來存儲帶有函數映射的數組和一個逗號運算符來返回第二個步驟,而不須要從一個上下文移動到另外一個上下文(因爲閉包和操做順序)。

const mapObject = (arr, fn) =>
  (a => (
    (a = [arr, arr.map(fn)]), a[0].reduce((acc, val, ind) => ((acc[val] = a[1][ind]), acc), {})
  ))();
const squareIt = arr => mapObject(arr, a => a * a);
squareIt([1, 2, 3]); // { 1: 1, 2: 4, 3: 9 }

offset

將指定數量的元素移動到數組的末尾。

兩次使用 Array.prototype.slice() 來獲取指定索引以後的元素和指定索引以前的元素。
使用展開操做符(...)將兩個數組合成一個數組。
若是 offset 爲負數,元素將從結束移動到開始位置。

const offset = (arr, offset) => [...arr.slice(offset), ...arr.slice(0, offset)];
offset([1, 2, 3, 4, 5], 2); // [3, 4, 5, 1, 2]
offset([1, 2, 3, 4, 5], -2); // [4, 5, 1, 2, 3]

pull

改變原始數組,過濾掉指定的值。

使用 Array.prototype.filter()array.prototype.include() 過濾指定的值。
使用 Array.prototype.length = 0 經過將數組的長度重置爲0來清空數組,並使用 array.prototype.push() 把提取的值從新填充數組。

(對於不改變原始數組的代碼片斷,請參閱 without)

const pull = (arr, ...args) => {
  let argState = Array.isArray(args[0]) ? args[0] : args;
  let pulled = arr.filter((v, i) => !argState.includes(v));
  arr.length = 0;
  pulled.forEach(v => arr.push(v));
};
let myArray = ['a', 'b', 'c', 'a', 'b', 'c'];
pull(myArray, 'a', 'c'); // myArray = [ 'b', 'b' ]

reducedFilter

根據條件過濾一個對象數組,同時過濾掉未指定的鍵。

使用 array.prototype.filter() 根據斷言函數 fn 對數組進行過濾,返回條件爲真值(truthy)的對象。
在通過過濾後的數組上,使用 array.prototype.map()array.prototype.reduce() 過濾掉在 keys 參數中未提供的鍵。

const reducedFilter = (data, keys, fn) =>
  data.filter(fn).map(el =>
    keys.reduce((acc, key) => {
      acc[key] = el[key];
      return acc;
    }, {})
  );
const data = [
  {
    id: 1,
    name: 'john',
    age: 24
  },
  {
    id: 2,
    name: 'mike',
    age: 50
  }
];

reducedFilter(data, ['id', 'name'], item => item.age > 24); // [{ id: 2, name: 'mike'}]

xProd

將兩個數組的每一個元素兩兩進行組合,組合出全部的可能對存在數組中,返回一個存在全部可能性對的數組。

使用 Array.prototype.reduce(), Array.prototype.map()Array.prototype.concat() 從兩個數組的元素中生成全部可能的對,並將它們保存在一個數組中。

const xProd = (a, b) => a.reduce((acc, x) => acc.concat(b.map(y => [x, y])), []);
xProd([1, 2], ['a', 'b']); // [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]

看完後,是否是以爲實現特簡潔特簡單?

看完後,給個 star 哦!倉庫地址:https://github.com/WYseven/30...

若是對你有幫助,請關注【前端技能解鎖】:
qrcode_for_gh_d0af9f92df46_258.jpg

相關文章
相關標籤/搜索