破壊的・非破壊的メソッドは知っておこう

2021-07-19

概要

JavaScriptで破壊的・非破壊的といったなんとなく強そうな言葉を耳にしたことがあると思います。
破壊的・非破壊的メソッドは何を表すものか、知っておくと可読性や思わぬバグを防ぐことも可能です。

定義

破壊的メソッドとは、オブジェクトそのものに変更を加えるメソッドです。
非破壊的メソッドとは、オブジェクトそのものに変更を加えないメソッドです。

破壊的・非破壊メソッドの比較

では比較していきながら解説していきます。

その1 pushメソッド

pushメソッドは配列にたいして、直接要素を追加します。
それゆえに破壊的メソッドといえます。

直感的ですが、結果の変数がどうなったのかがわかりづらく、バグの元になる危険性をはらみます。

const alphabets = ['a', 'b', 'c'];

// pushまでに長い処理があると仮定
const newAlphabets = alphabets.push('d')

// このころにはalphabetsが何を格納しているか不明瞭になっているでしょう
console.log(alphabets)

では非破壊的に書き換えるとどうなるでしょうか。
concatメソッドを使用してみます。

const alphabets = ['a', 'b', 'c'];
// concatメソッドは非破壊的メソッド
const newAlphabets = alphabets.concat('d')

console.log(alphabets) // ["a", "b", "c"] 
console.log(newAlphabets) // ["a", "b", "c", "d"] 

このように元の配列を変更することなく新しい配列を生成することができます。

またスプレッド構文を使用する方法があります。

const newAlphabets = [...alphabets, 'd']

その2 reverseメソッド

reverseメソッドは与えられた配列を反転させて、書き換えるものです。
これも破壊的メソッドです。

const word = ['h', 'e', 'l', 'l', 'o']
const reverse = word.reverse()
console.log(word) // ["o", "l", "l", "e", "h"]
console.log(reverse) // ["o", "l", "l", "e", "h"]

非破壊的に書き換えると、スプレッド構文を使用します。

const word = ['h', 'e', 'l', 'l', 'o']
// スプレッド構文によるシャローコピーがなされる
const reverse = [...word].reverse()
console.log(word) // ["h", "e", "l", "l", "o"]
console.log(reverse) // ["o", "l", "l", "e", "h"]

スプレッド構文はシャローコピーできるので、新しいコピー先にreverseメソッドを当てればよいです。

非破壊がいいようにみえるが

今までの事例から、破壊的は恐ろしいもの、非破壊が安全なもの、という印象がついてしまった方も多いと思います。
ですが、パフォーマンス的には破壊的のほうが早いです。

非破壊は新たに作成するので、その分遅くなる傾向にあります。

そんなに大きいプロジェクトやコードでは問題ないですが、複雑なコードや大量のデータを扱う際には注意が必要になってきます。

結論

破壊的・非破壊的を理解すると、保持しているオブジェクトの状態を意識してコーディングすることができます。

今回紹介したメソッドはほんの一握りです。参照に一通りのチートシートを作成している方がいらしたので、ご参考に。

余談 Reduxライブラリでは非破壊しか通用しない

これは完全余談ですが、ReactとセットででるのがReduxです。
状態管理ライブラリですが、単体でも使用できます。

状態のステートを変更するためにreducer関数を通して変更するのですが、その際に破壊的では変更を検知できません。
Reduxの変更検知は、オブジェクトの参照のみをチェックしているからです。
なので、元のプロパティの値のみを更新したとて、参照は変わらないので検知ができない、という構造です。

これはReducerの中身ですが、以下の場合だと変更を検知できません。

const initialState = [
  {
    id: 1,
    name: 'hoge'
  }
]
export default (state = initialState, action) => {
  switch (action.type) {
    case 'ADD':
      state.push({ ...action })
      return state;
    default:
      return state;
  }
}

これを検知させたければ、新しく作成したオブジェクトを返却する必要があります。

// 省略
export default (state = initialState, action) => {
  switch (action.type) {
    case 'ADD':
      // スプレッド構文にてシャローコピー
      return [...state, ...action];
      // もしくはsliceにて
      // const copyState = state.slice()
      // copyState.push({ ...action })
      // return copyState
    default:
      return state;
  }
}

といったように、Reduxでは状態検知にて破壊非破壊の使用が鍵になってきます。

参照

[JavaScript] Arrayメソッド破壊・非破壊チートシート
https://qiita.com/Shokorep/items/929e2e66908eaa915286

運営について

Natural Tearoomはシステム開発会社フロントエンドエンジニアがんちゃんが運営するメディアです。
フロントエンド技術を中心に発信しています。

· プライバシーポリシー

SNS

© 2021 天然珈琲店