かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JavaScript 配列から要素を取り出す slice と splice のメモ

state を扱っていると配列の操作をする機会が多いのですが配列から値を取り出す時の slicesplice 似てる。

Array.prototype.slice

arr.slice([begin[, end]])

slice は取り出す位置 (begin) から終了位置 (end) までの要素を取り出す。
戻り値は取り出された値の配列。
終了位置の要素は含まれず、end が省略さた場合は、begin から配列の最後 (length) までが指定される。
配列は shallow コピーされるので元の配列は変更されない。

e.g.

const arr = ['星宮いちご', '霧矢あおい', '紫吹蘭', '大空あかり', '氷上すみれ', '新条ひなき'];
const akari = arr.slice(3, 4); // => ['大空あかり']
const luminas = arr.slice(3); // => ['大空あかり', '氷上すみれ', '新条ひなき']

// 新しい配列が返されるので元の配列に変化はなし
console.log(arr);
// => ['星宮いちご', '霧矢あおい', '紫吹蘭', '大空あかり', '氷上すみれ', '新条ひなき']

// 引数なしで配列のコピーができる
const clone = arr.slice();
clone.reverse().push('神崎美月');
console.log(clone);
// => ['新条ひなき', '氷上すみれ', '大空あかり', '紫吹蘭', '霧矢あおい', '星宮いちご', '神崎美月']
console.log(arr);
// => ['星宮いちご', '霧矢あおい', '紫吹蘭', '大空あかり', '氷上すみれ', '新条ひなき']
shallow copy なので object などの参照になってる値には注意が必要
const arr = [
  { name: '星宮いちご', type: 'cute' },
  { name: '霧矢あおい', type: 'cool' },
  { name: '紫吹蘭', type: 'sexy' },
  { name: '有栖川おとめ', type: 'pop' },
];

// 配列をコピー
const copy = arr.slice();
// コピーした配列の操作やインデックスの値を丸っと置き換えるのは問題ない
copy.pop();
copy[2] = { '新条ひなき', type: 'pop' };
console.log(copy);
/*
[
  {name: "星宮いちご", type: "cute"},
  {name: "霧矢あおい", type: "cool"},
  {name: "新条ひなき", type: "pop"},
]
*/
console.log(arr);
/*
[
  {name: "星宮いちご", type: "cute"},
  {name: "霧矢あおい", type: "cool"},
  {name: "紫吹蘭", type: "sexy"},
  {name: "有栖川おとめ", type: "pop"},
]
*/

// 配列内のオブジェクトは参照のままなので変更すると元の配列の値に影響がある
copy[0].name = '大空あかり';
copy[1].name = '氷上すみれ';
console.log(copy);
/*
[
  {name: "大空あかり", type: "cute"},
  {name: "新条ひなき", type: "cool"},
  {name: "新条ひなき", type: "pop"},
]
*/
console.log(arr);
/*
[
  {name: "大空あかり", type: "cute"},
  {name: "氷上すみれ", type: "cool"},
  {name: "紫吹蘭", type: "sexy"},
  {name: "有栖川おとめ", type: "pop"},
]
*/

Array.prototype.splice

array.splice(start[, deleteCount[, item1[, item2[, ...]]]])

splice は開始位置 (start)と取り出す個数 (deleteCount) を指定して配列から要素を削除して取り出す。
戻り値は取り出された値の配列で、取り出された値は元の配列から削除される。(破壊的変更)

const arr = ['星宮いちご', '霧矢あおい', '紫吹蘭', '大空あかり', '氷上すみれ', '新条ひなき'];
const luminas = arr.splice(3, 3);
console.log(luminas);
// => ["大空あかり", "氷上すみれ", "新条ひなき"]

// 元の配列も変更される
console.log(arr);
// => ["星宮いちご", "霧矢あおい", "紫吹蘭"]

// 第2引数以降に値を渡すと取り出した位置に値を追加できる
const soleil = arr.splice(0,2, '神崎美月', '藤堂ユリカ');
console.log(soleil);
// => ["星宮いちご", "霧矢あおい"]

console.log(arr);
// => ["神崎美月", "藤堂ユリカ", "紫吹蘭"]

所感

配列な state を変更する際に、元の state を変更せずに要素を取り出すには slice 、値を取り出した残りの配列を使いたい場合は splice を使うのが良さそう。 splice を使う場合も [...state].splice として元の配列をコピーして操作するのが immutable な感じで良いかなーと思います。
それにしても何で slice は終了位置で、 splice は要素数なんだろう。紛らわしくない?


[参考]

確かな力が身につくJavaScript「超」入門 第2版

確かな力が身につくJavaScript「超」入門 第2版

アイカツ!成分が足りない…