TENTIALのテックブログ

株式会社TENTIALのエンジニアチームがECや開発や組織のよもやまを謳っていきます

実務で学んだJavaScript Tips集

自己紹介

はじめまして。TENTIALというD2CスタートアップでwebエンジニアをやっていますAmpiです。
エンジニア歴は今年で2年目となり、TENTIALでは主にバックエンド業務を行なっています。

記事を書くに至った背景

TENTIALは、フロントにNuxt、バックエンドでExpressを採用しており、フロントもバックエンドもJavaScriptを使用しています。
必然的にJavaScriptを書く機会が多くなり、初期と比べてJavaScriptの知識も少しは増えてきたかなと思ったので、今回記事をまとめてみました。
Tips集などと銘打ってますが、基本的な文法に関する事項が多いので、その点はご了承お願いしますmm

分割代入

配列やオブジェクトを分解し、配下の要素やプロパティ値を個々の変数として宣言できます。

基本的な使い方は、下記の通りです。

  • 基本
const numbers = [7, 5, 3]
const[a, b, c] = numbers
console.log(a) // 7
console.log(b) // 5
console.log(c) // 3

// やってることは下記と同じ
// const a = numbers[0]
// const b = numbers[1]
// const c = numbers[2]
const user = {
  name: '笹本希', 
  age: 33, 
  gender: 'female',
}

const { name, age } = user
console.log(name) // 笹本希
console.log(age) // 33

// やってることは下記と同じ
// const name = user.name
// const age = user.age

僕が業務で書く中で知ったのは下記2点です。

  • 既定値を与えることができる

  • ネストした分割代入も可能

具体的には、下記のように書くことができます。

  • 既定値を定めた配列の分割代入
const numbers = [7, 5]
const[a = 0, b = 0, c = 0 ] = numbers
console.log(a) // 7
console.log(b) // 5
console.log(c) // 0 = 既定値

// 配列numbersには2つの要素しかありませんが、既定値を定めて分割代入しているので、cがundefinedになりません。
  • ネストしたオブジェクトの分割代入
const user = {
  name: '笹本希', 
  age: 33, 
  gender: 'female',
  info: {
    id: 1
  }
}

const { gender, info: { id } } = user
console.log(gender) // female
console.log(id) // 1

勿論オブジェクトの分割代入する時も、既定値を定めることができます。

  • 既定値を定めたネストしたオブジェクトの分割代入
const order = {
  name: 'insole', 
  price: 7980, 
}

const { info: { id = 1 } = {} } = order
console.log(id) // 1 = 既定値

// orderオブジェクトにinfoプロパティが定義されてませんが、既定値を定めて分割代入しているので、エラーになりません。

個人的に特に気に入っているのは、既定値を定めたネストしたオブジェクトの分割代入です。 非同期処理などで、データの取得が上手くいかない場合を考慮して、if文を書いていたのですが、分割代入を利用することで、綺麗に書けるケースが増えた気がします。 また、バックエンド側でリクエストを受け取って、変数宣言する時等に分割代入を使用すると、無駄な重複を減らして、コードも簡潔に書けると思います。

スプレッド演算子

配列やオブジェクトを演算子が置かれた場所で展開することができます。

基本的な使い方は、下記の通りです。

  • 基本
const array1 = [1, 2, 3]
const array2 = [4, 5, 6]
const array3 = [...array1, ...array2]
console.log(array3) // 1, 2, 3, 4, 5, 6
const user = {
  name: '豊田翼', 
  age: 28,
}
console.log(user)  // { name: '豊田翼', age: 28 }

const params = {
  ...user,
  gender: 'female'
}
console.log(params) // { name: '豊田翼', age: 28, gender: 'female' }

スプレッド演算子は至る所で使いますが、個人的に好きなのは関数の引数でスプレッド演算子を使うことです。

  • 関数の引数でスプレッド演算子
const sumNumbers = (...args) => { 
  let sum = 0
  for (let arg of args) sum += arg
  return sum
}
console.log(sumNumbers(1, 2)) //3
console.log(sumNumbers(1, 2, 3)) //6

引数でスプレッド演算子を使うことで汎用性の高いメソッドを作れたりするので、良いなと思います。

あとこれはTipsっぽい技ですが、一意な配列を作成したい時に、スプレッド演算子とnew Set()を使うと、簡潔にコードを書けるケースがあります。

  • スプレッド演算子とnew Set() の併せ技
const array1 = [1, 2, 3]
const array2 = [...array1, 1, 2, 3,  4]
const uniqueArray = new Set([...array1, ...array2])
console.log(array2) // 1, 2, 3, 1, 2, 3, 4
console.log(uniqueArray) // 1, 2, 3, 4

ちなみにnew Set() は弊社テックリードに教えていただいた、素晴らしいメソッドなので、一意な配列を作成する時にfilterindexOfを併用している方は、是非一度チェックしてみて下さい。
(ちなみに僕は割と最近までfilterindexOfを使って一意な配列を作成していました^ ^)

実はよく知られてないreduce

配列の各要素を順番に累積して1つの値にできます。

最後は最早ただのメソッドなのですが、最近までしっかりと使いこなせていなかった、reduceメソッドについて少し深く書いていきたいと思います。
(意外と使いどころが多く、お気に入りメソッドなので紹介します。)
基本的な使い方は下記の通りです。

  • 基本
// 構文
array.reduce(callback [, initialValue])

const array1 = [1, 2, 3]

const sum = array1.reduce( (accumulator, currentValue, currentIndex, array) => {
    return accumulator + currentValue
}, 0) // 0はinitialValue

console.log(sum) // 6

ここで、accumulator, currentValue, currentIndex, array, initialValueについて、簡単な説明を書いておきます。

  • accumulator 累積値。わかりにくいのでコードで説明していきます。

  • currentValue 現在の値。配列の各要素

  • currentIndex 現在のindex。配列の添字

  • array 引数として配列を渡せる。(僕はあまり使ったことがないです。)

  • initialValue 初期値。既定値は0。引数に渡すと一番最初の関数実行時、accumulatorinitialValueの値が入ります。

ちなみにcurrentIndex, array, initialValueは省略することもあります。

  • 一部引数省略
const array1 = [1, 2, 3]
const sum = array1.reduce( (accumulator, currentValue) => {
   return accumulator + currentValue
}) 

console.log(sum) // 6

僕は最近までaccumulatorがあまり理解できてなかったので、reduceは合計値を出すときに使えば良いんだな程度の認識でした。
(しかし具体的にどういう処理が行われてるかイメージできずに、気持ち悪く感じているメソッドでした。)

そこで、今回はreduceの説明のために、letforEachを使って、reduceを書き換えたいと思います。
具体例は下記の通りです。

  • forEachreduce(initialValue なし)
/// reduceを使った場合
const array1 = [1, 2, 3]
const sum = array1.reduce( (accumulator, currentValue) => {
   return accumulator + currentValue
}) 
console.log(sum) // 6

// forEachを使った場合
let accumulator = 0
array1.forEach(value => {
  accumulator += value
})
console.log(accumulator) // 6

accumulatorの役割が少しイメージできたのではないでしょうか? 下記コードは結果的に同じ出力結果となります。

  • forEachreduce(initialValue あり)
const array1 = [1, 2, 3]
// reduceを使った場合
const sum = array1.reduce( (accumulator, currentValue) => {
    return accumulator + currentValue
}, 10) 
console.log(sum) // 16

// letとforEachを使った場合
let accumulator = 10
array1.forEach(value => {
  accumulator += value
})
console.log(accumulator) // 16

上記コードを簡単に説明すると、一番最初の関数実行時のaccumulatorは10で、配列をループさせながら、accumulatorに配列の各要素を足していくという挙動になります。

下記のような書き換えもできます。

  • if文とreduce
const array = [1, 2, 3, 4, 5]

// reduceを使った場合。配列の最後の要素だけ削除した配列を作る
const array1 = array.reduce((accumulator, currentValue, currentIndex) => {
  if(currentIndex !== array.length - 1){
    accumulator.push(currentValue)
  }
  return accumulator
}, [])

console.log(array1) // [1, 2, 3, 4]

// forEachを使った場合
let array2 = []
array.forEach((currentValue, currentIndex)=> {
  if(currentIndex !== array.length - 1){
    array2.push(currentValue)
  }
})

console.log(array2) // [1, 2, 3, 4]

個人的には、letforEachで書いていたコードが、constで書き換えれるのは大きな魅力だと感じています。
まだまだ小さなTipsが多くあるのですが、今回は以上となります。
慣れない技術ブログで、拙い点も多くあるかと思いますが、誰かの参考になれば幸いですmm

最後にエンジニア募集してます!!