@@ -659,9 +659,11 @@ console.log(array.length); // => 0
659659
660660## 破壊的なメソッドと非破壊的なメソッド {#mutable-immutable}
661661
662- これまで紹介してきた配列を変更するメソッドには、破壊的なメソッドと非破壊的メソッドがあります。この破壊的なメソッドと非破壊的メソッドの違いを知ることは、意図しない結果を避けるために重要です。
663- 破壊的なメソッドとは、配列オブジェクトそのものを変更し、変更した配列または変更箇所を返すメソッドです。
664- 非破壊的メソッドとは、配列オブジェクトのコピーを作成してから変更し、そのコピーした配列を返すメソッドです。
662+ これまで紹介してきた配列を変更するメソッドには、破壊的なメソッドと非破壊的メソッドがあります。
663+ この破壊的なメソッドと非破壊的メソッドの違いを知ることは、意図しない結果を避けるために重要です。
664+
665+ 破壊的なメソッド(Mutable Method)とは、配列オブジェクトそのものを変更し、変更した配列または変更箇所を返すメソッドです。
666+ 非破壊的メソッド(Immutable Method)とは、配列オブジェクトのコピーを作成してから変更し、そのコピーした配列を返すメソッドです。
665667
666668<!-- 具体例:破壊的なメソッド -->
667669
@@ -698,7 +700,7 @@ console.log(myArray === newArray); // => false
698700```
699701
700702
701- JavaScriptにおいて破壊的なメソッドと非破壊的メソッドを名前から見分ける方法はありません 。
703+ JavaScriptにおいて破壊的なメソッドと非破壊的メソッドを名前から見分けるのは難しいという問題があります 。
702704また、配列を返す破壊的なメソッドもあるため、返り値からも判別できません。
703705たとえば、Arrayの` sort ` メソッドは返り値がソート済みの配列ですが破壊的メソッドです。
704706
@@ -748,7 +750,8 @@ console.log(array); // => ["A", "C"]
748750一方、非破壊的メソッドは配列のコピーを作成するため、元々の配列に対して影響はありません。
749751この` removeAtIndex ` 関数を非破壊的なものにするには、受け取った配列をコピーしてから変更を加える必要があります。
750752
751- JavaScriptには` copy ` メソッドそのものは存在しませんが、配列をコピーする方法としてArrayの` slice ` メソッドと` concat ` メソッドが利用されています。` slice ` メソッドと` concat ` メソッドは引数なしで呼び出すと、その配列のコピーを返します。
753+ JavaScriptには` copy ` メソッドそのものは存在しませんが、配列をコピーする方法としてArrayの` slice ` メソッドと` concat ` メソッドが利用されています。
754+ ` slice ` メソッドと` concat ` メソッドは引数なしで呼び出すと、その配列のコピーを返します。
752755
753756{{book.console}}
754757``` js
@@ -783,9 +786,78 @@ console.log(newArray); // => ["A", "C"]
783786console .log (array); // => ["A", "B", "C"]
784787```
785788
786- このようにJavaScriptの配列には破壊的なメソッドと非破壊的メソッドが混在しています。そのため、統一的なインターフェースで扱えないのが現状です。
787- このような背景もあるため、JavaScriptには配列を扱うためのさまざまライブラリが存在します。
788- 非破壊的な配列を扱うライブラリの例として[ immutable-array-prototype] [ ] や[ Immutable.js] [ ] などがあります。
789+ このようにJavaScriptの配列には破壊的なメソッドと非破壊的メソッドが混在しています。
790+ 名前からも区別することが難しく、副作用を避けるためにコピーを作ってから破壊的メソッドを使うというパターンが利用されていました。
791+
792+ しかし、ES2023でこの状況を改善する変更が追加されています。
793+ 今まで、破壊的なメソッドしかなかった、` splice ` 、` reverse ` 、` sort ` に対して、
794+ 非破壊的なバージョンである` toSpliced ` 、` toReversed ` 、` toSorted ` が追加されました。
795+
796+ これらの` to ` から始まる非破壊的メソッドが受け取る引数は破壊的なメソッドと同じですが、非破壊的に変更した配列を返す点が異なります。
797+ 次のコードの` toSpliced ` メソッドは、配列を複製してから変更するため、元々の配列である` array ` には影響を与えていないことがわかります。
798+
799+ {{book.console}}
800+ <!-- doctest:meta:{ "ECMAScript": "2023" } -->
801+ ``` js
802+ const array = [" A" , " B" , " C" ];
803+ // `toSpliced`は`array`を複製してから変更する
804+ const newArray = array .toSpliced (1 , 1 );
805+ console .log (newArray); // => ["A", "C"]
806+ // コピー元の`array`には影響がない
807+ console .log (array); // => ["A", "B", "C"]
808+ ```
809+
810+ 先ほど` removeAtIndex ` 関数の実装では、` slice ` メソッドで配列をコピーしてから` splice ` メソッドを呼び出していました。
811+ 次のコードでは、` toSpliced ` メソッドを使うことで、より簡潔に非破壊的な` removeAtIndex ` 関数を実装しています。
812+
813+ {{book.console}}
814+ <!-- doctest:meta:{ "ECMAScript": "2023" } -->
815+ ``` js
816+ // `array`の`index`番目の要素を削除した配列を返す関数
817+ function removeAtIndex (array , index ) {
818+ // コピーを作成してから変更する
819+ return array .toSpliced (index, 1 );
820+ }
821+ const array = [" A" , " B" , " C" ];
822+ // `array`から1番目の要素を削除した配列を取得
823+ const newArray = removeAtIndex (array, 1 );
824+ console .log (newArray); // => ["A", "C"]
825+ // 元の`array`には影響がない
826+ console .log (array); // => ["A", "B", "C"]
827+ ```
828+
829+ また、ES2023では配列の指定したインデックスの要素を非破壊的に変更する` with ` メソッドも追加されました。
830+ ` array[index] = value ` の代入処理は、元々の配列を変更する破壊的な処理です。
831+ これに対して` with ` メソッドは、配列を複製してから指定したインデックスの要素を変更した配列を返す非破壊的なメソッドです。
832+
833+ {{book.console}}
834+ <!-- doctest:meta:{ "ECMAScript": "2023" } -->
835+ ``` js
836+ const array = [" A" , " B" , " C" ];
837+ // `array`の1番目の要素を変更した配列を返す
838+ const newArray = array .with (1 , " B2" );
839+ console .log (newArray); // => ["A", "B2", "C"]
840+ ```
841+
842+ 次の表では、破壊的な方法に対応する非破壊的な方法をまとめています。
843+
844+ | 破壊的な方法 | 非破壊な方法 |
845+ | ---------------------------------------- | ------------- |
846+ | ` array[index] = item ` | [ ` Array.prototype.with ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/with ) <sup >[ ES2023] </sup > |
847+ | [ ` Array.prototype.pop ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/pop ) | [ ` array.slice(0, -1) ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/slice ) と[ ` array.at(-1) ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/at ) <sup >[ ES2022] </sup > |
848+ | [ ` Array.prototype.push ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/push ) | ` [...array, item] ` <sup >[ ES2015] </sup > |
849+ | [ ` Array.prototype.splice ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/splice ) | [ ` Array.prototype.toSpliced ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced ) <sup >[ ES2023] </sup > |
850+ | [ ` Array.prototype.reverse ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse ) | [ ` Array.prototype.toReversed ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/toReversed ) <sup >[ ES2023] </sup > |
851+ | [ ` Array.prototype.sort ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort ) | [ ` Array.prototype.toSorted ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted ) <sup >[ ES2023] </sup > |
852+ | [ ` Array.prototype.shift ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/shift ) | [ ` array.slice(1) ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/slice ) と[ ` array.at(0) ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/at ) <sup >[ ES2022] </sup > |
853+ | [ ` Array.prototype.unshift ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift ) | ` [item, ...array] ` <sup >[ ES2015] </sup > |
854+ | [ ` Array.prototype.copyWithin ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin ) <sup >[ ES2015] </sup > | なし |
855+ | [ ` Array.prototype.fill ` ] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/fill ) <sup >[ ES2015] </sup > | なし |
856+
857+ 破壊的なメソッドは、シンプルですが元の配列も変更してしまうため、意図しない副作用が発生しバグの原因となる可能性があります。
858+ 非破壊的なメソッドは、使い分けが必要ですが元の配列を変更せずに新しい配列を返すため、副作用が発生することはありません。
859+
860+ そのため、まず非破壊的な方法で書けるかを検討し、そうではない場合に破壊的な方法を利用するとよいでしょう。
789861
790862## 配列を反復処理するメソッド {#array-iterate}
791863
0 commit comments