あまり意識していなかった破壊的な配列メソッド
JavaScriptの配列メソッドには、破壊的なメソッド と 非破壊的なメソッド の2種類があり、意識して使っていましたか?
いくつかある 破壊的なメソッド のうち、次の4つ
「Array.prototype.push()」
「Array.prototype.pop()」
「Array.prototype.shift()」
「Array.prototype.unshift()」
をセットで覚えやすいのでピックアップしました。
破壊的なメソッド は、元のオブジェクトや配列を直接変更するため、意図しない場所でデータが変わってしまう副作用を引き起こす可能性があるため注意が必要です。
副作用 (side effect) とは
関数やメソッドが直接的な戻り値以外にプログラムの状態に影響を与えることを指す。
末尾で破壊するメソッド push()追加
pop()削除
配列の末尾に要素を追加し破壊する push()メソッド
配列の末尾から要素を削除し破壊する pop() メソッド
末尾の要素:push()追加 pop()削除
ちなみに、popには素早く取り出すという意味があります。
const animals = ['dog', 'monkey'];
const newLength = animals.push('bird');
console.log(newLength); // 3
// 注意) push()メソッドは配列に新しい要素を追加し、追加後の配列の長さを返す
console.log(animals); // ["dog", "monkey", "bird"]
// 元の配列が変わる破壊的配列
const animals = ['dog', 'monkey', 'bird'];
const animal = animals.pop(); //'bird'
// pop()メソッドは引数を受け取らず、配列の末尾から要素を削除する
console.log(animal);
// 注意) animalには、削除された要素である 'bird' が代入される。
console.log(animals); // ["dog", "monkey"]
// 元の配列が変わる破壊的配列
先頭で破壊するメソッド unshift()追加
shift()削除
配列の先頭に要素を追加し破壊する unshift() メソッド
配列の先頭から要素を削除し破壊する shift() メソッド
末尾の要素:unshift()追加 shift()削除
ちなみに、shiftには入れ替えるという意味があります。
const arr = [1, 2, 3];
const newLength = arr.unshift(4, 5);
console.log(newLength); // 5
// 注意) unshift()メソッドは配列の先頭に新しい要素を追加し、追加後の配列の長さを返す
console.log(arr); // [4, 5, 1, 2, 3]
//元の配列が変わる破壊的メソッド
const arr = [1, 2, 3];
const removeElement = arr.shift();
// shift()メソッドは引数を受け取らず、配列の先頭の削除された要素を返す
// また、配列が空の場合は、undefinedを返す
console.log(removeElement); // 1
console.log(arr); // [2, 3]
//元の配列が変わる破壊的メソッド
おまけ:slice()とsplice() 破壊的な配列メソッドはどっち?
Array.prototype.slice() → 非破壊的なメソッド
Array.prototype.splice() → 破壊的なメソッド
slice(start, end)
配列の一部を start から end (end は含まれない)までの範囲で、選択した新しい配列オブジェクトにシャローコピーして返します。つまり、元の配列は変更されません。
const foods = ['Avocado', 'Orange', 'Lemon', 'Tomato', 'Corn'];
const fruits = fruits.slice(1, 3);
console.log(foods);
// ['Avocado', 'Orange', 'Lemon', 'Tomato', 'Corn'];
console.log(fruits);
// ['Orange','Lemon']
// つまり、元の配列を破壊することなく欲しい要素を取り出せる
splice(start, deleteCount, item1, item2, ...,itemN)
破壊的なメソッド
既存の要素を取り除いたり、置き換えたり、新しい要素を追加したりすることで、配列の内容を変更します。
開始位置 (start)と取り出す個数 (deleteCount) を指定して配列から要素を削除して取り出します。
戻り値は取り出された値の配列で、取り出された値は元の配列から削除されます。
const ingredients = ['にんじん', 'じゃがいも', '玉ねぎ', 'トマト', 'チキン'];
ingredients.splice(3, 0, 'クミン');
console.log(ingredients);// ['にんじん', 'じゃがいも', '玉ねぎ', 'クミン', 'トマト', 'チキン'];
// 元の配列が変わる破壊的メソッド
// deleteCountが0や負の値の場合どの要素も取り除かれない
ingredients.splice(3, 1, 'なす');
console.log(ingredient);
// ['にんじん', 'じゃがいも', '玉ねぎ', 'なす', 'トマト', 'チキン']
// ↑ さらに配列が変わっている
指定の違いに注意
slice→範囲(endは含まない)
splice→開始位置+要素数+何を
おまけ:スプレッド構文、slice()を使おう
破壊的なメソッド は、元のオブジェクトや配列を直接変更するため、意図しない場所でデータが変わってしまう副作用を引き起こす可能性があるため、イミュータブルな(不変な)書き方をする必要があります。
特に、Reactでは、
「イミュータブルな操作」「破壊的な変更を伴う操作は避けること」が推奨され、
Reactコンポーネントのパフォーマンスの向上のための重要な原則です。
イミュータブルとは
既存のデータを変更せずに新しいデータを生成する操作のこと。
イミュータブル(不変)な書き方をすることで、元のデータ構造は変更されず、意図しない副作用を避けることができます。
破壊的な”追加”メソッドのpush()やunshift()の代わりにスプレッド構文を使う
// push()の様に末尾に追加したい場合
const num = [1, 2, 3];
const newNum = [...num, 4];
console.log(num); // [1, 2, 3]
console.log(newNum); // [1, 2, 3, 4]
// numは変更されない。
// スプレッド構文を使うことでnumはイミュータブルなものとして扱われる。
// unshift()の様に頭に追加したい場合
// 元の配列
const originalArray = [1, 2, 3, 4, 5];
// 新しい要素を配列の先頭に追加した新しい配列を作成
const newValue = 0;
const newArray = [newValue, ...originalArray];
console.log(originalArray); // [1, 2, 3, 4, 5]
console.log(newArray); // [0, 1, 2, 3, 4, 5]
破壊的な”削除”のメソッドのpop()、shift()の代わりに非破壊的メソッドのslice()を使う
// pop()の様に末尾を削除したい場合
const originalArray = [1, 2, 3, 4, 5];
// 最後の要素を除いた新しい配列を作成
const newArray = originalArray.slice(0, -1);
console.log(originalArray); // [1, 2, 3, 4, 5]
console.log(newArray); // [1, 2, 3, 4]
// shift()の様に先頭を削除したい場合
const originalArray = [1, 2, 3, 4, 5];
// 最初の要素を除いた新しい配列を作成
const newArray = originalArray.slice(1);
console.log(originalArray); // [1, 2, 3, 4, 5]
console.log(newArray); // [2, 3, 4, 5]
参考
すでに、破壊的メソッドは、
「push()」「pop()」「shift()」「unshift()」 、おまけで「splice()」 がありました。
他に代表的な破壊的メソッドはどんなものがあるのか気になると思いますが、意外にも多くありません。
気になった方は以下の記事も参考にしてみてください。
[JavaScript] Arrayメソッド破壊・非破壊チートシート