- Day 23:スプレッド構文(前半)
- スプレッド構文(…)とは何か
- 配列のコピー:元の配列を壊さないための基本テクニック
- 配列の結合:複数の配列を1つにまとめる
- オブジェクトのコピー:元のオブジェクトを壊さない
- オブジェクトのコピー+一部変更:新しい状態を作る
- 実践:データを変更せず新しいデータを作る
- Day 23 前半 練習テンプレート(ブラウザで実行可能)
- Day 23 前半のまとめ
- Day 23:スプレッド構文(後半)
- 元のデータを変えないことの意味
- 配列のコピーと結合の実践
- オブジェクトのコピーと一部変更の実践
- 配列+オブジェクト+スプレッド構文の組み合わせ
- 関数とスプレッド構文
- Day 23 後半 練習テンプレート(ブラウザで実行可能)
- Day 23 後半のまとめ
Day 23:スプレッド構文(前半)
「元のデータを壊さずに“新しいデータ”を作る力を身につける」
Day 23では、JavaScriptでとてもよく使われる スプレッド構文(...) を学びます。
学習内容は次のとおりです。
...(スプレッド構文)とは- 配列のコピー
- 配列の結合
- オブジェクトのコピー
- 実践:データを変更せず新しいデータを作る
ここで身につけてほしいのは、
「元のデータを直接書き換えず、新しいデータを作る」
という考え方です。 これは、バグを減らし、安全なコードを書くうえで非常に重要な考え方です。
スプレッド構文(…)とは何か
「中身を“展開する”ための記号」
スプレッド構文は、... という3つのドットで書きます。
ざっくり言うと、
配列やオブジェクトの「中身」を、別の場所に“展開”するための記法
です。
まずはイメージから見てみます。
const numbers = [1, 2, 3];
// スプレッド構文で中身を展開する
const copy = [...numbers];
console.log(copy); // [1, 2, 3]
console.log(numbers); // [1, 2, 3]
JavaScript[...]の中で...numbersと書くことで、numbersの中身(1, 2, 3)がそのまま展開されます。- 結果として、
copyはnumbersと同じ内容を持つ「別の配列」になります。
この「中身を展開する」という性質を使って、
- コピー(複製)
- 結合(くっつける)
- 一部だけ変更した新しいデータを作る
といったことができるようになります。
配列のコピー:元の配列を壊さないための基本テクニック
なぜ「コピー」が必要なのか
まず、よくある危険なパターンを見てみます。
const original = [1, 2, 3];
// 直接代入すると「同じ配列」を指すことになります
const wrongCopy = original;
wrongCopy.push(4);
console.log('original:', original); // [1, 2, 3, 4]
console.log('wrongCopy:', wrongCopy); // [1, 2, 3, 4]
JavaScriptwrongCopy = original;は「コピー」ではなく「同じものを指しているだけ」です。wrongCopyを変更すると、originalも一緒に変わってしまいます。
これは、バグの原因になりやすい危険な状態です。
スプレッド構文で「安全なコピー」を作る
const original = [1, 2, 3];
// スプレッド構文で中身を展開して、新しい配列を作ります
const copy = [...original];
copy.push(4); // copyだけに要素を追加
console.log('original:', original); // [1, 2, 3]
console.log('copy:', copy); // [1, 2, 3, 4]
JavaScriptcopyはoriginalとは別の配列です。copyを変更しても、originalは変わりません。
重要ポイント:
「元の配列を壊したくないときは、まずスプレッド構文でコピーを作る」
という習慣をつけると、安全なコードに近づきます。
配列の結合:複数の配列を1つにまとめる
2つの配列をくっつける基本形
const a = [1, 2];
const b = [3, 4];
// スプレッド構文で中身を展開して、1つの配列にまとめます
const merged = [...a, ...b];
console.log(merged); // [1, 2, 3, 4]
JavaScript...aが[1, 2]の中身を展開...bが[3, 4]の中身を展開- それらを
[]の中で並べることで、1つの配列になります。
途中に別の値を挟むこともできる
const a = [1, 2];
const b = [5, 6];
const merged = [...a, 3, 4, ...b];
console.log(merged); // [1, 2, 3, 4, 5, 6]
JavaScript- スプレッド構文で展開した中に、普通の値も混ぜられます。
- 「既存の配列+新しい値+別の配列」という柔軟な結合ができます。
オブジェクトのコピー:元のオブジェクトを壊さない
危険なパターン:オブジェクトをそのまま代入する
const originalUser = {
name: '山田太郎',
age: 25,
};
// これは「コピー」ではなく「同じオブジェクト」を指しています
const wrongUserCopy = originalUser;
wrongUserCopy.age = 30;
console.log('originalUser:', originalUser); // ageが30に変わっている
console.log('wrongUserCopy:', wrongUserCopy); // ageが30
JavaScriptwrongUserCopyを変更すると、originalUserも変わってしまいます。- 「元のデータを残しておきたい」というときには危険です。
スプレッド構文でオブジェクトをコピーする
const originalUser = {
name: '山田太郎',
age: 25,
};
// スプレッド構文で中身を展開して、新しいオブジェクトを作ります
const userCopy = { ...originalUser };
userCopy.age = 30;
console.log('originalUser:', originalUser); // { name: '山田太郎', age: 25 }
console.log('userCopy:', userCopy); // { name: '山田太郎', age: 30 }
JavaScriptuserCopyはoriginalUserとは別のオブジェクトです。userCopyを変更しても、originalUserは変わりません。
オブジェクトのコピー+一部変更:新しい状態を作る
「元のデータ+変更部分」で新しいオブジェクトを作る
const originalUser = {
id: 101,
name: '佐藤花子',
age: 30,
isPremium: false,
};
// プレミアム会員に昇格した新しいユーザー情報を作りたい
const updatedUser = {
...originalUser, // 元のプロパティをすべてコピー
isPremium: true, // isPremiumだけ上書き
};
console.log('originalUser:', originalUser);
// { id: 101, name: '佐藤花子', age: 30, isPremium: false }
console.log('updatedUser:', updatedUser);
// { id: 101, name: '佐藤花子', age: 30, isPremium: true }
JavaScript...originalUserで元のプロパティをすべてコピーしています。- そのあとに
isPremium: trueと書くことで、 「元のデータをベースに、一部だけ変更した新しいオブジェクト」を作っています。
重要ポイント:
「元のデータはそのまま残しつつ、新しい状態を表すオブジェクトを作る」
という考え方は、 安全なデータ処理・バグの少ないコードにつながります。
実践:データを変更せず新しいデータを作る
ここからは、実務に近い形で「元のデータを壊さずに新しいデータを作る」例を見ていきます。
商品一覧に「新商品」を追加する(元の一覧はそのまま)
const products = [
{ id: 1, name: 'ノート', price: 100 },
{ id: 2, name: 'ペン', price: 150 },
];
const newProduct = { id: 3, name: '消しゴム', price: 80 };
// 元のproductsを壊さずに、新しい一覧を作ります
const updatedProducts = [...products, newProduct];
console.log('元のproducts:', products);
// 変更されていない
console.log('新しいproducts一覧:', updatedProducts);
// 3件目として消しゴムが追加されている
JavaScriptupdatedProductsは「新商品を含む新しい一覧」です。productsはそのまま残っています。
ユーザー設定を一部だけ変更した新しい設定を作る
const settings = {
theme: 'dark',
language: 'ja',
notifications: true,
};
// 通知だけオフにした新しい設定を作りたい
const newSettings = {
...settings,
notifications: false,
};
console.log('元のsettings:', settings);
// { theme: 'dark', language: 'ja', notifications: true }
console.log('新しいsettings:', newSettings);
// { theme: 'dark', language: 'ja', notifications: false }
JavaScript- 元の設定はそのまま残しつつ、 「通知だけオフにした新しい設定」を作っています。
Day 23 前半 練習テンプレート(ブラウザで実行可能)
以下のテンプレートをブラウザで開き、 コンソールで「配列のコピー・結合」「オブジェクトのコピー・一部変更」を確認してください。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Day 23 前半:スプレッド構文 練習テンプレート</title>
</head>
<body>
<h1>Day 23 前半:スプレッド構文(配列・オブジェクトのコピーと結合)</h1>
<p>ブラウザのコンソールを開いて、ログを確認してください。</p>
<script>
console.log('--- 配列のコピー(危険なパターン) ---');
const original = [1, 2, 3];
const wrongCopy = original; // 同じ配列を参照しているだけ
wrongCopy.push(4);
console.log('original(危険なコピー後):', original);
console.log('wrongCopy:', wrongCopy);
console.log('--- 配列のコピー(スプレッド構文) ---');
const safeOriginal = [1, 2, 3];
const safeCopy = [...safeOriginal]; // 安全なコピー
safeCopy.push(4);
console.log('safeOriginal:', safeOriginal); // [1, 2, 3]
console.log('safeCopy:', safeCopy); // [1, 2, 3, 4]
console.log('--- 配列の結合 ---');
const a = [1, 2];
const b = [3, 4];
const merged = [...a, ...b];
console.log('merged:', merged); // [1, 2, 3, 4]
console.log('--- オブジェクトのコピー(危険なパターン) ---');
const originalUser = { name: '山田太郎', age: 25 };
const wrongUserCopy = originalUser;
wrongUserCopy.age = 30;
console.log('originalUser(危険なコピー後):', originalUser);
console.log('wrongUserCopy:', wrongUserCopy);
console.log('--- オブジェクトのコピー(スプレッド構文) ---');
const safeUserOriginal = { name: '山田太郎', age: 25 };
const safeUserCopy = { ...safeUserOriginal };
safeUserCopy.age = 30;
console.log('safeUserOriginal:', safeUserOriginal); // ageは25のまま
console.log('safeUserCopy:', safeUserCopy); // ageは30
console.log('--- オブジェクトのコピー+一部変更 ---');
const user = {
id: 101,
name: '佐藤花子',
age: 30,
isPremium: false,
};
const updatedUser = {
...user,
isPremium: true,
};
console.log('元のuser:', user);
console.log('更新後のupdatedUser:', updatedUser);
console.log('--- 商品一覧に新商品を追加(元の一覧はそのまま) ---');
const products = [
{ id: 1, name: 'ノート', price: 100 },
{ id: 2, name: 'ペン', price: 150 },
];
const newProduct = { id: 3, name: '消しゴム', price: 80 };
const updatedProducts = [...products, newProduct];
console.log('元のproducts:', products);
console.log('新しいproducts一覧:', updatedProducts);
console.log('--- 設定オブジェクトの一部だけ変更 ---');
const settings = {
theme: 'dark',
language: 'ja',
notifications: true,
};
const newSettings = {
...settings,
notifications: false,
};
console.log('元のsettings:', settings);
console.log('新しいsettings:', newSettings);
console.log('--- Day 23 前半:スプレッド構文の練習が完了しました ---');
</script>
</body>
</html>
Day 23 前半のまとめ
Day 23前半では、
- スプレッド構文(
...)が「中身を展開する」ための記法であること - 配列のコピーを安全に行う方法
- 配列同士を結合して新しい配列を作る方法
- オブジェクトのコピーを安全に行う方法
- 元のデータを壊さずに「一部だけ変更した新しいデータ」を作る方法
を確認しました。
ここでのキーワードは、
「元のデータを直接いじらず、新しいデータを作る」
という考え方です。 この考え方は、今後のJavaScript学習だけでなく、 大規模なアプリケーションやセキュアなコードを書くうえでも、とても重要な土台になります。
Day 23:スプレッド構文(後半)
「“元のデータを守りながら”新しいデータを作る実践編」
Day 23後半では、前半で学んだスプレッド構文(...)を、 もう一歩踏み込んで「実務に近い形」で使っていきます。
テーマは引き続き、
- 配列のコピー
- 配列の結合
- オブジェクトのコピー
- 実践:データを変更せず新しいデータを作る
ですが、ここでは 「なぜ元のデータを変えないことが大事なのか」 を意識しながら、 具体的なシナリオを通して理解を深めていきます。
元のデータを変えないことの意味
「“安全なコード”と“バグの少なさ”につながる考え方」
直接書き換えると何が困るのか
例えば、商品一覧を管理しているコードがあるとします。
const products = [
{ id: 1, name: 'ノート', price: 100 },
{ id: 2, name: 'ペン', price: 150 },
];
JavaScriptこの products を、画面表示用に少し加工したいとき、 もし「直接書き換え」をしてしまうと、後から別の処理が products を使ったときに、 「いつの間にか中身が変わっている」という状態になりかねません。
// 直接書き換えてしまう危険な例
products.push({ id: 3, name: '消しゴム', price: 80 });
// ここから先のコードは、「元のproductsが2件だけ」という前提で書かれているかもしれません
JavaScriptこうした「どこかで勝手に変わっている」状態は、 バグの原因になりやすく、セキュリティ的にも好ましくありません。
「元のデータは触らず、新しいデータを作る」という発想
そこで重要になるのが、
元のデータはそのまま残しておき、 必要に応じて「新しいデータ」を作る
という発想です。
スプレッド構文は、この発想をコードで実現するための強力な道具です。
配列のコピーと結合の実践
「履歴を残しながら、新しい状態を作る」
シナリオ:商品一覧の「更新履歴」を残したい
商品一覧を更新していくときに、 「前の状態も残しておきたい」ということがあります。
const productsV1 = [
{ id: 1, name: 'ノート', price: 100 },
{ id: 2, name: 'ペン', price: 150 },
];
const newProduct = { id: 3, name: '消しゴム', price: 80 };
// 新しいバージョン(productsV2)を作ります
const productsV2 = [...productsV1, newProduct];
console.log('初期バージョン productsV1:', productsV1);
console.log('更新後バージョン productsV2:', productsV2);
JavaScriptproductsV1は「初期状態」として残しておきます。productsV2は「新商品を追加した後の状態」です。- どちらも配列ですが、スプレッド構文のおかげで「別々の状態」として扱えます。
シナリオ:検索結果に「おすすめ商品」を先頭に追加する
検索結果の配列があり、 その先頭に「おすすめ商品」を追加したいとします。
const searchResults = [
{ id: 2, name: 'ペン', price: 150 },
{ id: 3, name: '消しゴム', price: 80 },
];
const recommended = { id: 1, name: 'ノート', price: 100 };
// 元のsearchResultsはそのままにして、表示用の配列を作ります
const displayResults = [recommended, ...searchResults];
console.log('元の検索結果:', searchResults);
console.log('表示用の検索結果:', displayResults);
JavaScriptdisplayResultsは「おすすめ商品を先頭にした新しい配列」です。searchResultsは「元の検索結果」としてそのまま残っています。
オブジェクトのコピーと一部変更の実践
「ユーザー情報・設定情報を安全に更新する」
シナリオ:ユーザーのステータスだけ変更したい
ユーザー情報を表すオブジェクトがあるとします。
const user = {
id: 101,
name: '佐藤花子',
email: 'hanako@example.com',
isPremium: false,
};
JavaScriptこのユーザーがプレミアム会員になったとき、 「元の情報は残しつつ、新しい状態を作る」ことを考えます。
const premiumUser = {
...user, // 元のプロパティをすべてコピー
isPremium: true // isPremiumだけ上書き
};
console.log('元のuser:', user);
console.log('プレミアム版 premiumUser:', premiumUser);
JavaScriptuserは「プレミアムになる前の状態」として残ります。premiumUserは「プレミアムになった後の状態」です。
シナリオ:設定オブジェクトの一部だけ変更したい
アプリの設定を表すオブジェクトがあるとします。
const settings = {
theme: 'dark',
language: 'ja',
notifications: true,
};
JavaScriptここで、「通知だけオフにした設定」を作りたい場合、 元の settings を直接書き換えるのではなく、 新しい設定オブジェクトを作ります。
const silentSettings = {
...settings,
notifications: false,
};
console.log('元のsettings:', settings);
console.log('通知オフのsilentSettings:', silentSettings);
JavaScriptsettingsは「通常の設定」として残ります。silentSettingsは「通知オフの設定」として別に存在します。
配列+オブジェクト+スプレッド構文の組み合わせ
「一覧データを安全に更新する」
シナリオ:ToDo一覧に新しいタスクを追加する
const todos = [
{ id: 1, title: 'JavaScriptの勉強', done: false },
{ id: 2, title: '買い物に行く', done: true },
];
const newTodo = { id: 3, title: 'メールの返信', done: false };
// 元のtodosはそのままにして、新しい一覧を作ります
const updatedTodos = [...todos, newTodo];
console.log('元のtodos:', todos);
console.log('更新後のupdatedTodos:', updatedTodos);
JavaScripttodosは「更新前の一覧」として残ります。updatedTodosは「新しいタスクを含む一覧」です。
シナリオ:特定のタスクだけ状態を変えた新しい一覧を作る
「idが2のタスクを完了にした新しい一覧」を作りたいとします。
const todos = [
{ id: 1, title: 'JavaScriptの勉強', done: false },
{ id: 2, title: '買い物に行く', done: false },
];
const updatedTodos = todos.map((todo) => {
if (todo.id === 2) {
// idが2のタスクだけ、doneをtrueにした新しいオブジェクトを返します
return {
...todo,
done: true,
};
}
// それ以外は元のままコピーして返します
return { ...todo };
});
console.log('元のtodos:', todos);
console.log('更新後のupdatedTodos:', updatedTodos);
JavaScriptmap()とスプレッド構文を組み合わせることで、 「一覧の中の一部だけ変更した新しい一覧」を作れます。- 元の
todosは一切変更されていません。
関数とスプレッド構文
「柔軟な引数・安全な更新ロジックを作る」
シナリオ:ユーザー設定を更新する関数
const defaultSettings = {
theme: 'light',
language: 'ja',
notifications: true,
};
// 部分的な更新内容を受け取り、新しい設定を返す関数
function updateSettings(currentSettings, partialUpdate) {
return {
...currentSettings, // 現在の設定をコピー
...partialUpdate, // 更新したい部分を上書き
};
}
const userSettings = { ...defaultSettings };
const newUserSettings = updateSettings(userSettings, {
theme: 'dark',
notifications: false,
});
console.log('元のuserSettings:', userSettings);
console.log('更新後のnewUserSettings:', newUserSettings);
JavaScriptupdateSettingsは「元の設定+更新内容」から新しい設定を作る関数です。- スプレッド構文を使うことで、 「どの部分を上書きするか」が明確になります。
Day 23 後半 練習テンプレート(ブラウザで実行可能)
以下のテンプレートをブラウザで開き、 コンソールで「元のデータを変更せず新しいデータを作る」流れを確認してください。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Day 23 後半:スプレッド構文 実践テンプレート</title>
</head>
<body>
<h1>Day 23 後半:スプレッド構文(データを変更せず新しいデータを作る)</h1>
<p>ブラウザのコンソールを開いて、ログを確認してください。</p>
<script>
console.log('--- 商品一覧のバージョン管理 ---');
const productsV1 = [
{ id: 1, name: 'ノート', price: 100 },
{ id: 2, name: 'ペン', price: 150 },
];
const newProduct = { id: 3, name: '消しゴム', price: 80 };
const productsV2 = [...productsV1, newProduct];
console.log('productsV1:', productsV1);
console.log('productsV2:', productsV2);
console.log('--- 検索結果におすすめ商品を追加 ---');
const searchResults = [
{ id: 2, name: 'ペン', price: 150 },
{ id: 3, name: '消しゴム', price: 80 },
];
const recommended = { id: 1, name: 'ノート', price: 100 };
const displayResults = [recommended, ...searchResults];
console.log('元のsearchResults:', searchResults);
console.log('表示用displayResults:', displayResults);
console.log('--- ユーザーのステータス更新(元のデータは保持) ---');
const user = {
id: 101,
name: '佐藤花子',
email: 'hanako@example.com',
isPremium: false,
};
const premiumUser = {
...user,
isPremium: true,
};
console.log('元のuser:', user);
console.log('premiumUser:', premiumUser);
console.log('--- 設定オブジェクトの一部だけ変更 ---');
const settings = {
theme: 'dark',
language: 'ja',
notifications: true,
};
const silentSettings = {
...settings,
notifications: false,
};
console.log('元のsettings:', settings);
console.log('silentSettings:', silentSettings);
console.log('--- ToDo一覧に新しいタスクを追加 ---');
const todos = [
{ id: 1, title: 'JavaScriptの勉強', done: false },
{ id: 2, title: '買い物に行く', done: true },
];
const newTodo = { id: 3, title: 'メールの返信', done: false };
const updatedTodos = [...todos, newTodo];
console.log('元のtodos:', todos);
console.log('updatedTodos:', updatedTodos);
console.log('--- 特定のToDoだけ状態を変更した新しい一覧を作る ---');
const todosForUpdate = [
{ id: 1, title: 'JavaScriptの勉強', done: false },
{ id: 2, title: '買い物に行く', done: false },
];
const updatedTodos2 = todosForUpdate.map((todo) => {
if (todo.id === 2) {
return {
...todo,
done: true,
};
}
return { ...todo };
});
console.log('元のtodosForUpdate:', todosForUpdate);
console.log('updatedTodos2:', updatedTodos2);
console.log('--- 設定更新関数の実践 ---');
const defaultSettings = {
theme: 'light',
language: 'ja',
notifications: true,
};
function updateSettings(currentSettings, partialUpdate) {
return {
...currentSettings,
...partialUpdate,
};
}
const userSettings = { ...defaultSettings };
const newUserSettings = updateSettings(userSettings, {
theme: 'dark',
notifications: false,
});
console.log('元のuserSettings:', userSettings);
console.log('newUserSettings:', newUserSettings);
console.log('--- Day 23 後半:スプレッド構文の実践が完了しました ---');
</script>
</body>
</html>
Day 23 後半のまとめ
Day 23後半では、
- 「元のデータを直接書き換えず、新しいデータを作る」ことの意味
- スプレッド構文で配列・オブジェクトをコピーし、安全に更新する方法
- 一覧データ(商品一覧・ToDo一覧など)を、 スプレッド構文と組み合わせて安全に更新する方法
- 関数の中でスプレッド構文を使い、「部分的な更新」をわかりやすく書く方法
を確認しました。
スプレッド構文は、 「データの安全性」と「コードの読みやすさ」を両立させるための武器」 だと意識して使っていくと、 実務レベルのコードに近づいていく感覚をつかめるはずです。
