MochiuWiki : SUSE, EC, PCB
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
JavaScriptの基礎 - スプレッド構文のソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
JavaScriptの基礎 - スプレッド構文
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == スプレッド構文 (<code>...</code>) は、ES2015 (ES6) で導入された構文であり、配列やオブジェクトの要素を個別の値に展開するために使用する。<br> <br> 配列に対してスプレッド構文を使用すると、配列の全要素を個別の値として展開できる。<br> これにより、配列の結合、コピー、関数への引数展開を簡潔に記述することができる。<br> <br> オブジェクトに対しても同様に使用でき、オブジェクトのプロパティを展開してコピーやマージを実現できる。<br> 後から指定したプロパティが優先されるため、特定のプロパティのみを上書きするパターンとしても広く活用される。<br> <br> スプレッド構文が行うコピーはシャローコピー (浅いコピー) であり、1レベル深くのみコピーする点に注意が必要である。<br> ネストされたオブジェクトや配列は参照がコピーされるため、元のオブジェクトと同じ参照を共有する。<br> <br> Reactの状態管理においては、スプレッド構文はイミュータブルな更新パターンの中心的な構文として使用される。<br> <code>useState</code> で管理するオブジェクトや配列を更新する時には、スプレッド構文で新しいオブジェクトを生成することが必須である。<br> <br> 同じ <code>...</code> 記法でも、使用する位置によって意味が異なる。<br> <br> 右辺や関数呼び出し時に使用した場合はスプレッド構文として「展開する」動作をし、左辺や関数パラメータで使用した場合は残余パターンとして <u>集約する</u> 動作をする。<br> <br><br> == 配列でのスプレッド構文 == ==== 配列の展開 ==== スプレッド構文を使用すると、配列の全要素を個別の値として展開できる。<br> 展開した値の前後に追加の要素を並べることで、先頭・末尾への要素追加も簡潔に記述できる。<br> <br> <syntaxhighlight lang="javascript"> const arr = [1, 2, 3]; // 配列の要素を展開して新しい配列を作成する const expanded = [...arr, 4, 5]; console.log(expanded); // [1, 2, 3, 4, 5] // 先頭に要素を追加する const withStart = [0, ...arr]; console.log(withStart); // [0, 1, 2, 3] // 末尾に要素を追加する const withEnd = [...arr, 4]; console.log(withEnd); // [1, 2, 3, 4] // 中間に挿入する const withMiddle = [0, ...arr, 4, 5]; console.log(withMiddle); // [0, 1, 2, 3, 4, 5] </syntaxhighlight> <br> ==== 配列のコピー (シャローコピー) ==== スプレッド構文を使用すると、配列のシャローコピーを簡潔に作成できる。<br> コピーされた配列は元の配列とは独立した別の配列であるため、要素の追加・削除は元の配列に影響しない。<br> <br> <syntaxhighlight lang="javascript"> const original = [1, 2, 3]; // スプレッド構文でシャローコピーを作成する const copy = [...original]; // copyを変更しても、originalは変化しない copy.push(4); console.log(original); // [1, 2, 3] console.log(copy); // [1, 2, 3, 4] // 配列の参照比較 console.log(original === copy); // false (別の配列オブジェクト) </syntaxhighlight> <br> ==== 配列の結合 ==== スプレッド構文を使用すると、複数の配列を結合した新しい配列を作成できる。<br> <code>Array.prototype.concat</code> の代替として使用でき、より直感的に記述できる。<br> <br> <syntaxhighlight lang="javascript"> const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const arr3 = [7, 8, 9]; // 2つの配列を結合する const combined = [...arr1, ...arr2]; console.log(combined); // [1, 2, 3, 4, 5, 6] // 3つ以上の配列を結合する const all = [...arr1, ...arr2, ...arr3]; console.log(all); // [1, 2, 3, 4, 5, 6, 7, 8, 9] // 結合時に追加の要素も含められる const withExtra = [...arr1, 0, ...arr2]; console.log(withExtra); // [1, 2, 3, 0, 4, 5, 6] </syntaxhighlight> <br><br> == オブジェクトでのスプレッド構文 == ==== オブジェクトの展開 ==== スプレッド構文はオブジェクトのプロパティを展開して、新しいオブジェクトを作成するためにも使用できる。<br> この機能はES2018で導入された。<br> <br> <syntaxhighlight lang="javascript"> const obj = { a: 1, b: 2 }; // オブジェクトのプロパティを展開して、新しいオブジェクトを作成する const expanded = { ...obj, c: 3 }; console.log(expanded); // { a: 1, b: 2, c: 3 } // 先頭にプロパティを追加する const withPrefix = { x: 0, ...obj }; console.log(withPrefix); // { x: 0, a: 1, b: 2 } </syntaxhighlight> <br> ==== オブジェクトのコピー (シャローコピー) ==== スプレッド構文を使用すると、オブジェクトのシャローコピーを作成できる。<br> コピーしたオブジェクトへのプロパティ追加・変更は、元のオブジェクトに影響しない。<br> <br> <u>ただし、値がオブジェクトや配列のプロパティについては参照のみがコピーされる点に注意が必要である。</u><br> <br> <syntaxhighlight lang="javascript"> const original = { a: 1, b: 2 }; // スプレッド構文でシャローコピーを作成する const copy = { ...original }; // copyのプロパティを変更しても、originalは変化しない copy.a = 100; console.log(original); // { a: 1, b: 2 } console.log(copy); // { a: 100, b: 2 } // オブジェクトの参照比較 console.log(original === copy); // false (別のオブジェクト) </syntaxhighlight> <br> ==== オブジェクトのマージ ==== スプレッド構文を使用すると、複数のオブジェクトを1つにマージできる。<br> <code>Object.assign</code> の代替として使用でき、より簡潔に記述できる。<br> <br> <syntaxhighlight lang="javascript"> const obj1 = { a: 1, b: 2 }; const obj2 = { c: 3, d: 4 }; const obj3 = { e: 5, f: 6 }; // 2つのオブジェクトをマージする const merged = { ...obj1, ...obj2 }; console.log(merged); // { a: 1, b: 2, c: 3, d: 4 } // 3つ以上のオブジェクトをマージする const all = { ...obj1, ...obj2, ...obj3 }; console.log(all); // { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 } </syntaxhighlight> <br> ==== プロパティの上書き ==== 複数のオブジェクトをスプレッド構文でマージする場合、同名のプロパティは後から指定した値で上書きされる。<br> この性質を利用すると、オブジェクトの特定のプロパティのみを更新した新しいオブジェクトを簡潔に作成できる。<br> <br> <syntaxhighlight lang="javascript"> const obj = { a: 1, b: 2, c: 3 }; // 特定のプロパティを上書きする // 後から指定したプロパティが優先される const updated = { ...obj, b: 20 }; console.log(updated); // { a: 1, b: 20, c: 3 } // 前に指定した場合は、objのプロパティに上書きされる const overwritten = { b: 0, ...obj }; console.log(overwritten); // { b: 2, a: 1, c: 3 } ← bはobjの値に上書きされる // 複数プロパティを同時に上書きする const multiUpdate = { ...obj, a: 10, c: 30 }; console.log(multiUpdate); // { a: 10, b: 2, c: 30 } </syntaxhighlight> <br><br> == 関数引数での展開 == スプレッド構文を使用すると、配列の要素を関数の個別の引数として展開できる。<br> <code>Function.prototype.apply</code> の代替として使用でき、より直感的に記述できる。<br> <br> <syntaxhighlight lang="javascript"> // Math.maxへの展開 const numbers = [1, 5, 3, 9, 2]; // applyを使用した従来の方法 const maxOld = Math.max.apply(null, numbers); // スプレッド構文を使用した方法 (推奨) const max = Math.max(...numbers); console.log(max); // 9 const min = Math.min(...numbers); console.log(min); // 1 </syntaxhighlight> <br> <syntaxhighlight lang="javascript"> // 自作関数への引数展開 function sum(a, b, c) { return a + b + c; } const args = [1, 2, 3]; console.log(sum(...args)); // 6 // 一部を固定して残りを展開する function greet(greeting, firstName, lastName) { return greeting + ", " + firstName + " " + lastName + "!"; } const nameParts = ["Alice", "Smith"]; console.log(greet("Hello", ...nameParts)); // "Hello, Alice Smith!" </syntaxhighlight> <br><br> == シャローコピーの注意点 == ==== ネストしたオブジェクトの問題 ==== スプレッド構文によるコピーはシャローコピーであり、1レベル深くのみコピーする。<br> ネストされたオブジェクトや配列は参照のみがコピーされるため、コピー先を変更すると元のオブジェクトにも影響が生じる。<br> <br> <syntaxhighlight lang="javascript"> const original = { name: "Alice", address: { city: "Tokyo", zip: "100-0001" } }; // シャローコピーを作成する const copy = { ...original }; // トップレベルのプロパティは独立している copy.name = "Bob"; console.log(original.name); // "Alice" (変化しない) // ネストされたオブジェクトは参照を共有している copy.address.city = "Osaka"; console.log(original.address.city); // "Osaka" (元のオブジェクトも変化する!) </syntaxhighlight> <br> ネストしたオブジェクトも安全にコピーするには、内側のオブジェクトに対しても個別にスプレッド構文を適用する。<br> <br> <syntaxhighlight lang="javascript"> const original = { name: "Alice", address: { city: "Tokyo", zip: "100-0001" } }; // ネストしたオブジェクトにも個別にスプレッドを適用する const safeCopy = { ...original, address: { ...original.address, city: "Osaka" } }; console.log(safeCopy.address.city); // "Osaka" console.log(original.address.city); // "Tokyo" (変化しない) </syntaxhighlight> <br> ==== ディープコピーの方法 ==== ネストの深さに関わらず全てのレベルを完全にコピーするディープコピーが必要な場合、以下の方法を使用する。<br> <br> * <code>structuredClone()</code> (推奨) *: モダンブラウザおよびNode.js v17以降で使用できる組み込み関数である。 *: 循環参照、<code>Date</code>、<code>Map</code>、<code>Set</code>、<code>ArrayBuffer</code> 等の複雑なデータ型にも対応している。 *: <br> * <code>JSON.parse(JSON.stringify())</code> *: 関数、<code>undefined</code>、<code>Symbol</code>、<code>Date</code> オブジェクト (文字列に変換される)、循環参照を含むオブジェクトには使用できない。 *: シンプルなJSONデータに対してのみ有効な方法である。 <br> <syntaxhighlight lang="javascript"> const original = { name: "Alice", address: { city: "Tokyo" }, hobbies: ["reading", "coding"] }; // structuredClone() によるディープコピー (推奨) const deepCopy1 = structuredClone(original); deepCopy1.address.city = "Osaka"; deepCopy1.hobbies.push("gaming"); console.log(original.address.city); // "Tokyo" (変化しない) console.log(original.hobbies); // ["reading", "coding"] (変化しない) // JSON.parse(JSON.stringify()) によるディープコピー // 関数・undefined・Symbol・循環参照が含まれる場合は使用不可 const deepCopy2 = JSON.parse(JSON.stringify(original)); deepCopy2.address.city = "Nagoya"; console.log(original.address.city); // "Tokyo" (変化しない) </syntaxhighlight> <br><br> == Reactでのイミュータブル更新パターン == ==== stateの更新 ==== Reactでは、<code>useState</code> で管理するオブジェクトを更新する時に、スプレッド構文を使用する。<br> <u>オブジェクトや配列を直接変更 (ミューテーション) すると、Reactが変更を検知できず再描画が発生しない。</u><br> <br> スプレッド構文で新しいオブジェクトを生成することにより、Reactが変更を検知して正しく再描画される。<br> <br> <syntaxhighlight lang="javascript"> import { useState } from "react"; function UserProfile() { const [user, setUser] = useState({ name: "Alice", age: 30, city: "Tokyo" }); // 特定のプロパティのみを更新する function handleAgeChange() { setUser({ ...user, age: 31 }); } // 関数形式 (前のstateを確実に参照できる、推奨) function handleCityChange(newCity) { setUser(prev => ({ ...prev, city: newCity })); } // ネストしたオブジェクトの更新 const [profile, setProfile] = useState({ name: "Alice", address: { city: "Tokyo", zip: "100-0001" } }); function handleAddressChange(newCity) { setProfile(prev => ({ ...prev, address: { ...prev.address, city: newCity } })); } return ( <div> <p>{user.name}, {user.age}歳, {user.city}</p> <button onClick={handleAgeChange}>年齢を更新</button> <button onClick={() => handleCityChange("Osaka")}>都市を変更</button> </div> ); } </syntaxhighlight> <br> ==== 配列のイミュータブル操作 ==== Reactで配列の state を更新する場合も、<code>push</code>・<code>splice</code> 等の破壊的メソッドは使用しない。<br> スプレッド構文、<code>map</code>、<code>filter</code> を使用して新しい配列を生成することが必須である。<br> <br> <syntaxhighlight lang="javascript"> import { useState } from "react"; function TodoList() { const [items, setItems] = useState([ { id: 1, text: "買い物をする", done: false }, { id: 2, text: "掃除をする", done: false } ]); // 要素を追加する (pushの代替) function handleAdd(newItem) { setItems([...items, newItem]); } // 要素を削除する (spliceの代替) function handleDelete(targetId) { setItems(items.filter(item => item.id !== targetId)); } // 特定の要素を更新する (mapとスプレッドを組み合わせる) function handleUpdate(targetId, newText) { setItems(items.map(item => item.id === targetId ? { ...item, text: newText } : item )); } // 完了状態をトグルする function handleToggle(targetId) { setItems(items.map(item => item.id === targetId ? { ...item, done: !item.done } : item )); } return ( <ul> {items.map(item => ( <li key={item.id}> {item.text} <button onClick={() => handleDelete(item.id)}>削除</button> <button onClick={() => handleToggle(item.id)}>完了</button> </li> ))} </ul> ); } </syntaxhighlight> <br> 下表に、破壊的メソッドとイミュータブルな代替方法を示す。<br> <br> <center> {| class="wikitable" |+ 破壊的メソッドとイミュータブルな代替 ! 操作 !! 破壊的メソッド (使用不可) !! イミュータブルな代替 |- | 末尾に追加 || <code>push(item)</code> || <code>[...arr, item]</code> |- | 先頭に追加 || <code>unshift(item)</code> || <code>[item, ...arr]</code> |- | 要素を削除 || <code>splice(i, 1)</code> || <code>arr.filter(item => item.id !== id)</code> |- | 要素を更新 || <code>arr[i] = newItem</code> || <code>arr.map(item => item.id === id ? { ...item, ...updates } : item)</code> |- | ソート || <code>sort()</code> || <code>[...arr].sort()</code> |- | 逆順 || <code>reverse()</code> || <code>[...arr].reverse()</code> |} </center> <br><br> == スプレッド構文と残余パターンの違い == スプレッド構文と残余パターンは、どちらも同じ <code>...</code> 記法を使用するが、使用する位置によって動作が異なる。<br> <br> * スプレッド構文 (展開) *: 右辺または関数呼び出し時に使用し、配列やオブジェクトを個別の値に「展開する」動作をする。 * 残余パターン (集約) *: 左辺または関数パラメータで使用し、残りの要素を配列やオブジェクトに「集約する」動作をする。 <br> <syntaxhighlight lang="javascript"> // スプレッド構文 - 展開する (右辺、関数呼び出し時) const arr = [1, 2, 3]; const expanded = [...arr, 4, 5]; // 配列を展開する const obj1 = { a: 1 }; const merged = { ...obj1, b: 2 }; // オブジェクトを展開する Math.max(...arr); // 関数引数として展開する </syntaxhighlight> <br> <syntaxhighlight lang="javascript"> // 残余パターン : 集約する (左辺、関数パラメータ) // 配列の分割代入での残余パターン const [first, ...rest] = [1, 2, 3, 4, 5]; console.log(first); // 1 console.log(rest); // [2, 3, 4, 5] // オブジェクトの分割代入での残余パターン const { a, ...others } = { a: 1, b: 2, c: 3 }; console.log(a); // 1 console.log(others); // { b: 2, c: 3 } // 関数パラメータでの残余パターン function sum(...args) { return args.reduce((total, n) => total + n, 0); } console.log(sum(1, 2, 3, 4, 5)); // 15 </syntaxhighlight> <br> 下表に、スプレッド構文と残余パターンの違いを示す。<br> <br> <center> {| class="wikitable" |+ スプレッド構文と残余パターンの比較 ! 項目 !! スプレッド構文 !! 残余パターン |- | 記法 || <code>...</code> || <code>...</code> |- | 動作 || 展開する (1つ → 複数) || 集約する (複数 → 1つ) |- | 使用位置 || 右辺、関数呼び出し時 || 左辺、関数パラメータ |- | 使用例 || <code>[...arr, 4]</code>、<code>fn(...args)</code> || <code>const [a, ...rest] = arr</code>、<code>function fn(...args)</code> |} </center> <br><br> == 関連情報 == * [[JavaScriptの基礎 - オブジェクトリテラル]] *: オブジェクトの作成と操作、プロパティアクセス、短縮記法 * [[JavaScriptの基礎 - 分割代入]] *: オブジェクト/配列の分割代入、デフォルト値、残余パターン * [[JavaScriptの基礎 - 関数宣言と関数式]] *: 関数宣言と関数式、デフォルト引数、残余引数 <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:Web]]
JavaScriptの基礎 - スプレッド構文
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse