はじめに
自身のおさらいも含め、初学者の方に向けて、JavaScriptでの配列操作についてまとめていきたいと思います。
[対象読者]
JavaScript / React初学者
[記事のテーマ]
mutable / immutableとは?
フロントエンド開発における、非破壊的な配列操作について
Reactでアプリケーションを作っていく際は、副作用のない純粋な関数を使用することが推奨されます。純粋な関数であるためには、以下の条件が必要です。
・最低1つの引数を持つ
・1つの値、もしくは1つの関数を返す
・引数を変化させてはいけない (immutable) ← 今回の記事のテーマ
mutable, immutableとは?
JSの配列操作等のキーワードで検索をかけると、**mutable(破壊的なメソッド)とimmutable(非破壊的なメソッド)**というキーワードを良く目にするかと思いますが、破壊的なメソッドとは、対象となる元の配列の値を変えてしまうメソッドのことを指します。
以下に破壊的なメソッドと非破壊的なメソッドの違いを示します。
破壊的なメソッドは元の配列を直接操作(=破壊)する
const nums = [1,2,3,4,5];
// 〜何らかの処理〜
nums.push(6);
// 〜何らかの処理〜
console.log(nums);
// 読み手は[1,2,3,4,5]を期待する
非破壊的なメソッドは、元の配列のコピーに対して配列操作する
const nums = [1,2,3,4,5];
// 〜何らかの処理〜
const newNums = nums.concat(6);
// 〜何らかの処理〜
console.log(nums);
// [1,2,3,4,5] 読み手の期待通り
console.log(newNums)
// [1,2,3,4,5,6] こちらも読み手の期待通り
上記のように、破壊的なメソッドを使用して配列操作すると、不要な混乱を招くことになります。JavaScriptには、いくつか破壊的なメソッドが用意されており、以下メソッドを使う時は、注意が必要です。
splice()
push()
pop()
shift()
unshift()
sort()
reverse()
fill()
copyWithin()
破壊的な配列操作をする際も、以下のように、元の配列を変えてしまわないようにスプレッド演算子でコピーを作って操作できないか検討してみましょう。以下はsort()
を使用する際に、スプレッド演算子を使用して、破壊的な操作を回避した例です。
const articleIds = [3,5,1,4,2];
const sortedArticleIds = [...articleIds].sort();
console.log(sortedArticleIds);
// [1,2,3,4,5]
ユースケース別の実装例
1.配列を加工したい
ES6には複数の便利メソッドが用意されてますが、ここではよく利用されるであろうmap()
を紹介します。
まずは破壊的な操作
let nums = [1, 2, 3];
for (var i = 0; i < nums.length; i++) {
nums[i] = nums[i] * 2;
}
console.log(nums); // [ 2, 4, 6 ]
上記を非破壊的な操作に書き換えると以下になります。
const nums = [1, 2, 3];
const doubled = nums.map((num) => {
return num * 2;
});
console.log(nums); // [ 1, 2, 3 ]
console.log(doubled); // [ 2, 4, 6 ]
2.要素を追加したい
push()
unshift()
まずは、破壊的なメソッド。
// push()
const nums_push = [1,2,3,4,5];
nums_push.push(6);
console.log(nums_push); // [1,2,3,4,5,6]
// unshift()
const nums_unshift = [1,2,3,4,5];
nums_unshift.unshift(0);
console.log(nums_unshift); // [0,1,2,3,4,5]
非破壊的な操作に書き直します。
// popを非破壊的に書き直す
const nums = [1,2,3,4,5];
const newNums = [...nums, 6];
console.log(nums); // 1,2,3,4,5
console.log(newNume); // 1,2,3,4,5,6
// unshiftを非破壊的に書き直す
const nums = [1,2,3,4,5];
const newNums = [0, ...nums];
console.log(nums); // 1,2,3,4,5
console.log(newNume); // 0,1,2,3,4,5
3.要素を削除したい
pop()
shift()
まずは、破壊的なメソッド。
// pop()
const nums_pop = [1,2,3,4,5];
nums_pop();
console.log(nums_pop); // [1,2,3,4]
// shift()
const nums_shift = [1,2,3,4,5];
nums_unshift.shift();
console.log(nums_unshift); // [2,3,4,5]
非破壊的な操作に書き直します。
const nums = [1,2,3,4,5];
const newNums = nums.filter(num => num !== 3)
console.log(newNums) // 1,2,4,5
最後に
以上、駆け足ではありましたが、mutable / immutableな配列操作の違いの解説でした。相互レビューをする際に、不要な破壊的操作を行っているコードは指摘されるポイントになるので、非破壊的な操作は意識しながら開発するのが良いと思います。何故immutableである方が望ましいのかは別記事でもう少し掘り下げようかと思います。