Lodash.jsを使いこなしてJavaScript上級者を目指す
## 伝えたいこと
* Lodash.jsは最高
* Lodash.jsを使うとコードがシンプルになる
* Lodash.jsはメソッドチェインを使わないと魅力80%減
* Lodash.jsは最高
## Lodash.jsとは?
https://lodash.com/
* JavaScriptユーティリティライブラリ
* つまり便利なライブラリ
* 週2000万ダウンロードという超人気ライブラリ(jQueryは200万、Reactは500万)
* Underscore.jsより高速かつ高機能
## 使い方
まずnpmパッケージインストール。
```bash
npm i lodash
# または
yarn add lodash
```
それを読み込んで_(アンダーバー)で使う。
```js
import _ from 'lodash'
const result = _.compact([0, 1, false, 2, '', 3])
console.log(result) // [1, 2, 3]
```
## undefined/NaN対策
JavaScriptを書いていると苦労するのがundefinedやNaN対策。
```js
let arr;
console.log(arr.length) // Error
let obj;
console.log(obj.hoge) // Error
let num;
console.log(1 / num) // NaN
```
エラーにならないように下記のような回避コードを書くことが多かった。
```js
let arr;
console.log(Array.isArray(arr) ? arr.length : 0) // 0
let obj;
if ( typeof obj === 'object' && obj !== null && !Array.isArray(obj) ) {
console.log(obj.hoge)
} else {
console.log(undefined) // undefined
}
let num;
console.log(num > 0 ? 1 / num : 1) // 1
```
Lodash.jsならこの問題を下記のようにシンプルに解決できる。
```js
let arr;
console.log(_.size(arr)) // 0
let obj;
console.log(_.get(obj, "hoge")) // undefined
let num;
console.log(_.divide(1, num)) // 1
```
もちろん、ちゃんとエラーを返してほしいケースもあるし、Lodash.jsの関数が返す返り値が微妙と感じるなら使わないほうがいいが、ほとんどのケースではLodash.jsを使ったほうがコードの可読性が上がる。
## メソッドチェイン
Lodash.jsはメソッドチェインという使い方ができる。
例えば下記のように、ある配列に対して様々な操作をしたい場合、無駄なコードが多くDRYではない。
```js
let result = [1,2,3,4,5]
result = _.concat(result, [3,4,5,6,7]) // 別の配列を結合
result = _.uniq(result) // 重複する要素を削除
result = _.shuffle(result) // ランダムにシャッフル
console.log(result) // [6,2,7,1,3,5,4]
```
このコードにメソッドチェインを用いると下記のように書ける。
```js
const result = _.chain([1,2,3,4,5])
.concat([3,4,5,6,7])
.uniq()
.shuffle()
.value()
console.log(result) // [6,2,7,1,3,5,4]
```
メソッドチェインは可読性が上がるだけでなく、下記メリットがある。
* 変数再代入が発生しない
* 変数の数を減らせる
* 副作用が起こりにくい
つまり、関数型プログラミングのようなメリットを享受できるのが特徴。
## よく使う機能
特によく使う機能をいくつかご紹介。
### _.compact
配列の中からfalsyな要素(0, false, ""など)をすべて除外してくれる
```js
_.compact([0, 1, false, 2, '', 3])
// => [1, 2, 3]
```
### _.difference
2つの配列のうち第一引数の配列にしかない要素を残す
```js
_.difference([4, 2, 1], [2, 3])
// => [1, 4]
```
### _.differenceBy
2つの配列を関数またはプロパティ名で比較して第一引数の配列にしかない要素を残す
```js
_.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor)
// => [1.2]
const arr1 = [{ 'x': 2 }, { 'x': 1 }]
const arr2 = [{ 'x': 1 }, { 'x': 3 }]
_.differenceBy(arr1, arr2, 'x')
// => [{ 'x': 2 }]
```
### _.intersection
2つの配列の中で共通している要素を残す
```js
_.intersection([2, 1], [2, 3])
// => [2]
```
### _.union
2つの配列を重複のない状態で結合する
```js
_.union([2], [1, 2])
// => [2, 1]
```
### _.xor
2つの配列の中で共通している要素を除外し、片方にしか存在しない要素を残す
```js
_.xor([2, 1], [2, 3])
// => [1, 3]
```
### _.uniq
配列の中の重複する要素を1つだけ残して除外する
```js
_.uniq([2, 1, 2])
// => [2, 1]
```
### _.without
配列の中から指定した値を除外する
```js
_.without([2, 1, 2, 3], 1, 2)
// => [3]
```
### _.flattenDeep
ネストされた配列をすべてフラットにする
```js
_.flattenDeep([1, [2, [3, [4]], 5]])
// => [1, 2, 3, 4, 5]
```
### _.cloneDeep
オブジェクトを複製する(ネストされたオブジェクトも含めて全て値渡しする)
```js
const obj1 = { 'a': 1, 'b': { 'c': 2 } }
const obj2 = obj1
obj2.b.c = 3
console.log(obj1.b.c) // 3
const obj3 = { 'a': 1, 'b': { 'c': 2 } }
const obj4 = _.cloneDeep(obj3)
obj4.b.c = 3
console.log(obj3.b.c) // 2
```
### _.isPlainObject
純粋なオブジェクト型かどうかを判定する
```js
_.isPlainObject([1, 2, 3])
// => false
_.isPlainObject({ 'x': 0, 'y': 0 })
// => true
_.isPlainObject(Object.create(null))
// => true
```
### _.round
指定した桁数で四捨五入する
```js
_.round(4.006)
// => 4
_.round(4.006, 2)
// => 4.01
_.round(4060, -2)
// => 4100
```
### _.range
指定した要素数でカウントアップまたはカウントダウンする配列を生成
```js
_.range(4)
// => [0, 1, 2, 3]
_.range(-4)
// => [0, -1, -2, -3]
_.range(1, 5)
// => [1, 2, 3, 4]
_.range(0, 20, 5)
// => [0, 5, 10, 15]
_.range(0, -4, -1)
// => [0, -1, -2, -3]
_.range(1, 4, 0)
// => [1, 1, 1]
_.range(0)
// => []
```
### _.debounce
関数が呼び出されてから指定した秒数以内に再度関数が呼び出された場合は経過秒数をリセットし、関数を実行しない
```js
function handleScroll () {
console.log("handleScroll()")
}
const _handleScroll = _.debounce(handleScroll, 2000)
window.addEventListener('scroll', _handleScroll)
```