Chrome 110 で配列メソッドが追加された
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ 配列の破壊的メソッドの非破壊的版メソッドが追加された
◆ sort → toSorted, splice → toSpliced, reverse → toReversed
◆ インデックス指定で書き換えた版の with が追加された
◆ slice の手間が省けたくらいだけど with はメソッドチェーンで書けるので便利
◆ with は存在しないインデックスを指定するとエラーが起きるので場合によっては使えない
◆ sort → toSorted, splice → toSpliced, reverse → toReversed
◆ インデックス指定で書き換えた版の with が追加された
◆ slice の手間が省けたくらいだけど with はメソッドチェーンで書けるので便利
◆ with は存在しないインデックスを指定するとエラーが起きるので場合によっては使えない
この機能です
https://github.com/tc39/proposal-change-array-by-copy
Chrome 109 だと動かなくて 110 に更新したら動くようになったので 110 で追加のはずなのに Chrome Status では情報がないです
https://chromestatus.com/features#milestone%3D110
110 の更新内容には含まれてなくて 機能ごとの詳細ページを見ても 108 で DevTrial とだけ書かれていて Ship のバージョン情報がないです
https://chromestatus.com/feature/5068609911521280
ときどきこういうのがありますが Chrome Status のサイトの情報はあまり更新されてないのでしょうか
個別にバグトラッカーのサイトまで見るのは面倒なのでここにまとまっていてほしいです
sort, reverse, splice はその配列自体を変更してしまうので 元を保持したいなら slice などでコピーする必要がありました
これらにコピー不要で新しい配列を作る版のメソッドが増えました
また 直接インデックス指定で書き換えの新しい配列を作る版の with も追加されています
みたいにメソッドチェーンで書けて .slice() を挟むだけだったのであえて入れる必要ある?と思う内容でしたが toSpliced と with は一手間必要だったので嬉しい追加機能です
splice は sort や reverse と違って変更された配列と返り値は一致しません
変更したときに削除された要素の配列が返ってきます
toSpliced では変更された配列の方が返ってくるので toSpliced 相当のものが欲しい場合は一時変数に入れて配列の方を取得する必要があります
with は toSpliced でカバーできてる機能ですが よりわかりやすくかけます
これまでの方法でやろうとすると
か
みたいに少し読みづらい map をするか一時変数が必要になっていたのでこれをスッキリ書けるのは助かります
できればオブジェクトでも with メソッドを追加してほしいですが オブジェクトへのメソッド追加は影響が大きいのとスプレッド構文で十分とも言えるのでされない気がしますね
展開する部分の式が長くなるとチェーンに含められる方が見やすい気はしますけど
あとネストに対応して 深い部分のプロパティを変更したら全体の新しいオブジェクトを取得できるなら価値はありそうですが 実現はされなそうですね
軽く issue を見た感じ議論には出てそうだったので なにかの理由で通らなかったのでしょう
実際 concat や slice で代替できて 作るならこんな感じで簡単に作れるものですからね
でもそれをいえば sort だって .slice() 挟むだけなんですけどね
push や pop は返す値が配列の長さや取り出されたものなので 変更後の配列を返すのは目的が変わってしまうとかあるのかな と思いましたが splice は取り出された配列を return するのに toSpliced では取り出された配列は取得できず変更後の配列を返してます
そう考えるとやっぱり toPushed とかはあってもいいのではと思います
この手の API だとライブラリではオブジェクトで渡せば複数を一気に変更できそうなものですができません
みたいなことは無効です
のようにひとつずつ変更が必要です
ただ with ごとに内部的にはコピーが作られるわけで 要素数の多い配列で多くの with を繰り返すのは無駄が多くパフォーマンスに影響しそうです
slice でコピーして直接代入する方法だとこういうエラーは起きないのでこういう制限があるのは不便です
インデックスの範囲を超えていても気にしないのが JavaScript らしさだと思うのですが こういうのをエラーにされても不便なだけです
その割にインデックスに文字列など不正な数値を渡してもエラーにはならず 0 として処理されるのでこの辺はゆるいままです
内部で map を使う方法を呼び出してるとかで長さを変えることができないのでしょうか?
それでもエラーにされるよりは map を使うときの結果と同じように一致するのがないなら置き換えなしでただのコピーとしてくれたほうがいいのですけど
https://github.com/tc39/proposal-change-array-by-copy
Chrome 109 だと動かなくて 110 に更新したら動くようになったので 110 で追加のはずなのに Chrome Status では情報がないです
https://chromestatus.com/features#milestone%3D110
110 の更新内容には含まれてなくて 機能ごとの詳細ページを見ても 108 で DevTrial とだけ書かれていて Ship のバージョン情報がないです
https://chromestatus.com/feature/5068609911521280
ときどきこういうのがありますが Chrome Status のサイトの情報はあまり更新されてないのでしょうか
個別にバグトラッカーのサイトまで見るのは面倒なのでここにまとまっていてほしいです
追加された配列メソッド
追加された機能は 配列の破壊的メソッドに非破壊的メソッドが追加されたというものですsort, reverse, splice はその配列自体を変更してしまうので 元を保持したいなら slice などでコピーする必要がありました
これらにコピー不要で新しい配列を作る版のメソッドが増えました
const arr = [1, 2, 3]
console.log(arr.toSorted((a, b) => b - a))
// [3, 2, 1]
console.log(arr)
// [1, 2, 3]
const arr = [1, 2, 3]
console.log(arr.toReversed())
// [3, 2, 1]
console.log(arr)
// [1, 2, 3]
const arr = [1, 2, 3]
console.log(arr.toSpliced(1, 1, 4, 5))
// [1, 4, 5, 3]
console.log(arr)
// [1, 2, 3]
また 直接インデックス指定で書き換えの新しい配列を作る版の with も追加されています
const arr = [1, 2, 3]
console.log(a.with(1, 100))
// [1, 100, 3]
console.log(arr)
// [1, 2, 3]
比較
これまででもarr.slice().sort()
arr.slice().reverse()
みたいにメソッドチェーンで書けて .slice() を挟むだけだったのであえて入れる必要ある?と思う内容でしたが toSpliced と with は一手間必要だったので嬉しい追加機能です
splice は sort や reverse と違って変更された配列と返り値は一致しません
変更したときに削除された要素の配列が返ってきます
toSpliced では変更された配列の方が返ってくるので toSpliced 相当のものが欲しい場合は一時変数に入れて配列の方を取得する必要があります
const arr2 = arr.slice()
arr2.splice(1, 1, 100)
console.log(arr2)
with は toSpliced でカバーできてる機能ですが よりわかりやすくかけます
これまでの方法でやろうとすると
arr.map((value, index) => index === 1 ? 100 : value)
か
const copy = arr.slice()
copy[1] = 100
みたいに少し読みづらい map をするか一時変数が必要になっていたのでこれをスッキリ書けるのは助かります
できればオブジェクトでも with メソッドを追加してほしいですが オブジェクトへのメソッド追加は影響が大きいのとスプレッド構文で十分とも言えるのでされない気がしますね
const obj = { a: 1, b: 2}
const obj2 = obj.with("a", 100)
const obj3 = { ...obj, a: 100 }
展開する部分の式が長くなるとチェーンに含められる方が見やすい気はしますけど
あとネストに対応して 深い部分のプロパティを変更したら全体の新しいオブジェクトを取得できるなら価値はありそうですが 実現はされなそうですね
他の破壊的メソッド
どうせなら push, shift, fill なども非破壊的メソッドを追加して toPushed, toShifted, toFilled などがあるとよかったのですけどね軽く issue を見た感じ議論には出てそうだったので なにかの理由で通らなかったのでしょう
実際 concat や slice で代替できて 作るならこんな感じで簡単に作れるものですからね
Array.prototype.toPushed = function(...args) { return [...this, ...args] }
Array.prototype.toPopped = function() { return this.slice(0, -1) }
Array.prototype.toUnshifted = function(...args) { return [...args, ...this] }
Array.prototype.toShifted = function() { return this.slice(1) }
Array.prototype.toFilled = function(value) { return this.map(() => value) }
でもそれをいえば sort だって .slice() 挟むだけなんですけどね
push や pop は返す値が配列の長さや取り出されたものなので 変更後の配列を返すのは目的が変わってしまうとかあるのかな と思いましたが splice は取り出された配列を return するのに toSpliced では取り出された配列は取得できず変更後の配列を返してます
そう考えるとやっぱり toPushed とかはあってもいいのではと思います
with の不便なところ
まとめて変更できない
1 つの with で値を一つしか変更できないですこの手の API だとライブラリではオブジェクトで渡せば複数を一気に変更できそうなものですができません
arr.with({ 1: 100, 2: 200 })
みたいなことは無効です
arr.with(1, 100).with(2, 200)
のようにひとつずつ変更が必要です
ただ with ごとに内部的にはコピーが作られるわけで 要素数の多い配列で多くの with を繰り返すのは無駄が多くパフォーマンスに影響しそうです
値があるインデックスしか指定できない
値のないインデックスを指定するとエラーが起きますconst arr = [1, 2, 3]
arr.with(5, 100)
// Uncaught RangeError: Invalid index : 5
slice でコピーして直接代入する方法だとこういうエラーは起きないのでこういう制限があるのは不便です
インデックスの範囲を超えていても気にしないのが JavaScript らしさだと思うのですが こういうのをエラーにされても不便なだけです
その割にインデックスに文字列など不正な数値を渡してもエラーにはならず 0 として処理されるのでこの辺はゆるいままです
const arr = [1, 2, 3]
arr.with("foo", 100)
// [100, 2, 3]
内部で map を使う方法を呼び出してるとかで長さを変えることができないのでしょうか?
それでもエラーにされるよりは map を使うときの結果と同じように一致するのがないなら置き換えなしでただのコピーとしてくれたほうがいいのですけど