はじめに
ES6ではレスト演算子という機能が追加されました。レスト演算子はスプレッド演算子と同様に「…」で表現されますが、その役割は異なります。スプレッド演算子が配列やオブジェクトを展開するのに対し、レスト演算子は「複数のデータをまとめる」というスプレッド演算子とは逆の役割を果たします。
レスト演算子とスプレッド演算子の構文は似ているため、これらを合わせて「スプレッド構文」と呼ばれることがありますが、当サイトでは別のものとして扱っています。スプレッド演算子については以下の記事で詳しく解説しているので、これについて分からない方は参考にしていただけると幸いです。
本記事では、レスト演算子に焦点を当て、その基本的な使い方と注意点について解説します。初学者の方にもわかりやすいように、ES5以前との比較や具体的な例を交えながら、レスト演算子の概念やメリットを説明していきます。
この記事の対象者
- JavaScript初学者の方
- 「…」が分からない方
- 可読性を意識したい方
この記事で学べること
- レスト演算子の役割
- レスト演算子の使い方
- レスト演算子の注意点
ES5以前
レスト演算子は可変長引数を受け取る関数を定義する際に特に便利な機能です。レスト演算子を使用するメリットを理解するために、従来の方法ではどのように可変長引数を受け入れる関数を定義していたかを見てみましょう。ES5以前では以下のように記述する必要がありました。
// 引数の合計を計算して返す関数
function sum() {
let total = 0;
// argumentsオブジェクトを使用
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
// 関数を呼び出す
console.log(sum(1, 2, 3));
// 結果: 6
console.log(sum(1, 2, 3, 4, 5));
// 結果: 15
上記の例では、可変長引数を受け取るためにargumentsオブジェクトを使用しています。argumentsオブジェクトは関数に渡された引数を配列のように扱うことができますが、厳密には配列ではないためmap()やfilter()、forEach()といった配列のメソッドを直接使用できないなどの制約があります。そのため、コードがやや複雑になり、可読性があまり良くありません。
レスト演算子を使用することで、簡潔な表現で上記の関数を定義できます。
// 引数にレスト演算子を使用
function sum(...numbers) {
let total = 0;
// forEach()メソッドが使える
numbers.forEach(function(value) {
total += value;
});
return total;
}
上記のように、レスト演算子で受け取った引数は配列として扱われるためforEach()メソッドが利用できます。
ES6ではレスト演算子の他にも多くの機能が追加されており、その中に「アロー関数」というものがあります。アロー関数と配列用のreduce()メソッド併用することで、上記の関数は一行で記述することも出来ます。
// アロー関数と併用
const sum = (...numbers) => numbers.reduce((acc, curr) => acc + curr);
ES6で追加された機能は昨今のJavaScript開発に大きな影響を与え、開発をより快適に行うために多くの場面で利用されています。レスト演算子もその一つであり、これについて理解することは現在のJavaScriptを学ぶ上で必須と言えます。
アロー関数については以下の記事で詳しく解説しています。
ES6で追加された機能については以下の記事で詳しく解説しています。
レスト演算子の使い方
基本的なレスト演算子の使い方について解説します。レスト演算子は、主に以下の二つのケースで利用されます。
- 関数の引数での利用
- 分割代入での利用
それぞれについて、レスト演算子の基本的な使い方を見ていきます。
関数の引数での利用
レスト演算子は、関数の引数として使用されることが一般的です。スプレッド演算子と同様に「…」を使用しますが、役割は逆です。関数の引数でレスト演算子を使用することで、複数の引数を配列としてまとめることができ、可変長引数を受け取る関数を定義する際に便利です。以下はその例です。
function sum(...numbers) {
return numbers.reduce(function(acc, curr) {
return acc + curr;
});
}
console.log(sum(1, 2, 3));
// 結果: 6
console.log(sum(1, 2, 3, 4, 5));
// 結果:15
この例では、sum()関数は可変長の引数を受け取り、それらの合計を計算しています。関数の引数として定義された「…numbers」がレスト演算子になります。これにより、関数を呼び出す際に渡された全ての引数がnumbers配列にまとめられ、reduce()メソッドを使用してこの配列の要素の合計を計算しています。
分割代入での利用
ES6で追加された機能に「分割代入」というものがあります。これは配列やオブジェクトから部分的に値を取り出して変数に代入する方法です。分割代入内でレスト演算子を使用すると、配列やオブジェクトの「残りの部分」をまとめて取り出すことができます。
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first);
// 結果:1
console.log(second);
// 結果:2
// 残りの部分は配列として扱われます
console.log(rest);
// 結果:[3, 4, 5]
上記のように、配列の分割代入でレスト演算子を使用した場合は配列として変数に格納されます。続いて、オブジェクトの例を見てみます。
const person = {
name: 'taro',
age: 20,
hobby: ['Cocking', 'watching movies'],
skill: ['HTML', 'CSS', 'JavaScript']
}
// 分割代入にレスト演算子を使用
const { skill, ...other } = person;
console.log(skill);
// 結果:['HTML', 'CSS', 'JavaScript']
// otherにはskill以外のプロパティが格納されます
console.log(other);
// 結果:{name: 'taro', age: 20, hobby: ['Cocking', 'watching movies']}
上記のように、オブジェクトの分割代入でレスト演算子を使用した場合はオブジェクトとして変数に格納されます。
レスト演算子のポイント
レスト演算子はとても便利な機能で使い方もシンプルですが、ここで挙げるポイントについて理解しておくと効果的にレスト演算子を使うことができます。
レスト演算子は最後に書く
レスト演算子を関数の引数で利用する場合、そのパラメータは常に引数リストの最後に配置する必要があります。そうでない場合はエラーになります。
// 正しい使い方
function example(arg1, arg2, ...restArgs) {
…
}
// 間違った使い方
function invalidExample(arg1, ...restArgs, arg2) {
…
}
// エラー:Uncaught SyntaxError: Rest parameter must be last formal parameter
分割代入でも同様です。
// 正しい使い方
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// 間違った使い方
const [first, ...rest, second] = [1, 2, 3, 4, 5];
// エラー:Uncaught SyntaxError: Rest element must be last element
データが複数じゃなくても配列になる
レスト演算子を関数の引数で利用する場合、受け取る引数が無い場合や一つのみの場合でも配列として扱われます。
function example(...args) {
console.log(Array.isArray(args));
return args;
}
// 引数が無い場合
console.log(example());
/*
結果:
true
[]
*/
// 引数が一つの場合
console.log(example(1));
/*
結果:
true
[1]
*/
これは分割代入の場合でも同様です。
// 配列の場合
const emptyArray = [];
const [...array] = emptyArray;
console.log(Array.isArray(array));
// 結果:true
console.log(array);
// 結果:[]
// オブジェクトの場合
const emptyObject = {};
const {...object} = emptyObject;
console.log(object.constructor === Object);
// 結果:true
console.log(object);
// 結果:{}
レスト演算子は参照ではなくコピー
レスト演算子で代入した配列やオブジェクトは、元のデータを参照するのではなくコピーを生成します。そのため、レスト演算子で受け取った配列やオブジェクトを変更してもコピー元のデータは変更されません。
const numbers = [1, 2, 3, 4, 5];
const [first, ...rest] = numbers;
// restの値を変更してみます
rest.push(6);
rest[0] = 7;
// restの変更を確認します
console.log(rest);
// 結果:[7, 3, 4, 5, 6]
// numbersは変更されません
console.log(numbers);
// 結果:[1, 2, 3, 4, 5]
この仕様は安全で効率的ですが、膨大なデータに対してレスト演算子を多用した処理を実装するとパフォーマンスに影響を与える可能性があるため注意が必要です。
最後に
この記事では、レスト演算子に焦点を当て、その基本的な使い方と注意点について解説しました。レスト演算子を使用することで、関数の引数をまとめることができ、可変長引数を受け取る関数をより簡潔に定義することができます。また、分割代入で利用することで配列やオブジェクトの残りの部分を取り出す際にも便利です。
レスト演算子の使い方はシンプルなので、ES5以前の方法であるargumentsオブジェクトによる可変長引数の関数定義よりも簡潔であり、初学者の方でも積極的に利用できるオススメの機能です。
注意点として、レスト演算子は常に最後に配置する必要があることや、分割代入においては元データの参照ではなくコピーを生成することを意識する必要があります。
現在のJavaScript開発において、レスト演算子を含むスプレッド構文は頻繁に利用されており、この使い方を理解することでコードの可読性を向上させることができます。ぜひ活用してみてください。
