primordials.js を使ったベストプラクティス
Zenn に書いた プロトタイプ汚染周りの提案と primordials.js で触れたが、Node.js や Deno においてビルトインオブジェクトのグローバル、プロトタイプが変更されてしまっても API が実行できるように primordials.js というものが存在する。そこでのベストプラクティスをまとめる。 コレクション
Map, Set, WeakMap, WeakSet ではコンストラクタに引数を入れた場合 "add" や "set" メソッドに影響を受ける。primordials.js ではコンストラクタ、プロトタイプメソッドを全てラップした SafeMap などが提供されている。
イテレーター
ビルトインオブジェクトの @@iterator メソッドが変更されてしまった場合に影響を受けるため、予め {Array, %TypedArray%, String}#[@@iterator] をキャッシュしておいてそれを使うことになる。またその返り値のオブジェクトの持つ %ArrayIteratorPrototype%, %StringIteratorPrototype% の "next" メソッドの書き換えにも注意する必要がある。
primordials.js では専用の SafeArrayIterator, SafeStringIterator などが提供されている。for-of やスプレッド構文を配列で使う場合は new SafeArrayIterator(arr) のようにラップして使う。
ジェネレーター
ジェネレーター函数の返り値である Generator のもつ Generator#{next, return, throw} の変更に影響を受ける。ラッパーを用意するか、使わないのが無難。
instanceof
instanceof 演算子や Function#[@@hasInstance] を使った場合、対象となるクラスの "prototype" プロパティを取得して判定を行う。つまり "prototype" プロパティの変更に影響を受ける。さらにはクラスにメソッドを生やすと動作を書き換えられる。Object#isPrototypeOf と対象となるプロトタイプを予めキャッシュしておき、それを使うと安全。
Promise のスタティックメソッド
Promise#then に影響を受ける。Deno の primordials.js に SafePromiseAll などを入れて対応した。
RegExp を受け取る String メソッド
RegExp を受け取る String メソッドでは対応する RegExp のメソッドが呼び出される。つまり RegExp.prototype のメソッドが変更された場合に影響を受ける。Deno の primordials.js に SafeRegExp を入れて対応した。
Array#concat
引数のオブジェクトの @@isConcatSpreadable に影響される。使わないのが無難。
JSON.stringify
JSON.stringify は引数にオブジェクトもしくは BigInt が渡されてきたときに "toJSON" メソッドが実行されるようになっている。つまり {Object, BigInt}#toJSON を追加してしまえば簡単に挙動を変えることが出来る。対処法は Proxy でラップするか、渡すオブジェクトの [[Prototype]] を null にしちゃうとかかな。
{Array, %TypedArray%}#toString
{Array, %TypedArray%}#toString はほぼほぼ "join" メソッドのラッパーと言っても良い。"join" メソッドの書き換えに影響されるため、単に {Array, %TypedArray%}#join を使ったほうが良い。Deno の primordials.js では上書きして対応。