[Brazil調査] サーバスクリプトの JavaScript 構文:ES12 モードで動くものと動かないもの
Brazil にメジャーアップグレードしたあと、サーバスクリプトに ?. や ?? や class を書いても大丈夫なのでしょうか。公式のリリースノートには「ES12 モードで使える機能が増えた」とあるだけで、何が使えるのかは書かれていません。そこで、ES12 モードのスコープ付きアプリと従来どおりのグローバルで同じスニペットを動かし、使えた構文と使えなかった構文を並べました。
検証環境:Brazil PDI(Australia からメジャーアップグレード)/ OOTB / 2026-10-09 確認
前提:PDI は1台で、アップグレード前の Australia との比較はしていません。公式ドキュメントは Brazil 版の差分ノートと Australia 版の機能ページを使っています。
ES12 で動いた構文と動かなかった構文
ES12 モードのスコープ付きアプリでは、オプショナルチェーン、Null 合体、クラスフィールド、await が実行できた。Promise.allSettled などは関数として定義されていたが、呼び出しは試していない
公式の Australia 版の表で Not Supported の機能(private なインスタンスメソッドなど)が、この Brazil PDI では動いた。Australia との実機比較はしていない
グローバルの Scripts - Background は ES Level 0 で、?. と ?? は構文エラーになった
BigInt の ** は実行時エラーになり、WeakRef は未定義だった
公式の差分ノートは Australia と Brazil に同じ文面を載せており、Brazil で新しく増えた機能は特定できなかった
この PDI では、アプリ788件のうち564件が ES12、192件が ES5 Standards、17件が Compatibility で、15件は未設定だった
下の画像は、ES12 モードのスコープで8行のスクリプトを1回実行した結果です。

①の表示は、スクリプトがスコープ付きアプリ(JavaScript Mode が ECMAScript 2021)の中で実行されたことを示します。②の8行には、user?.address?.city が undefined になる動きと、new Set([1, 2]).union(...) の結果が並びます。||= や class のフィールドも値を返しました。
公式ドキュメントが説明する JavaScript モード
モードの効く範囲
JavaScript モードは、サーバスクリプトでどの ECMAScript の機能を使えるかを決める、設計時と実行時の設定です。JavaScript エンジンには ECMAScript 2021(ES12)、ES5 Standards、Compatibility の3つのモードがあります。既存のサーバスクリプトを壊さずに、新しい書き方も使えるようにするための仕組みです。
引用元:JavaScript engine on the platform(公式)
エンジンの更新に、プラグインやシステムプロパティは不要です。公式が挙げる利点は次のとおりです。オプショナルチェーン、分割代入、const と let、アロー関数といった現代的な書き方が使えること。
3つのモードの違い
Compatibility:ES5 Standards モードが入る前に作られたスクリプト向け。古いエンジン由来の挙動を保つ。サードパーティの JavaScript ライブラリは対象外
ES5 Standards:ECMAScript 5 の構文と機能。"use strict"、ネイティブの JSON、lodash や moment のようなライブラリを使える
ECMAScript 2021(ES12):ES12 と ES13 の構文・機能の一部。デフォルト引数、分割代入、アロー関数、クラス宣言、Map、Set、オプショナルチェーンなど
どこで設定するか
アプリ単位の設定は、アプリケーションレコードの「JavaScript Mode」フィールドです。ナビゲータで sys_app.list を開いてアプリを選び、フィールドでモードを選んで更新します。必要なロールは、委任された開発者のロール(フルアクセス)か admin です。
引用元:Set the JavaScript mode for an application(公式)
スクリプト単位の設定もあります。ES5 Standards か Compatibility のアプリでは、個々のスクリプトで「Turn on ECMAScript 2021 (ES12) mode」を選べます。スクリプトのフォームに出るチェックで、ES12 のアプリの中では読み取り専用。
引用元:Turn on ES12 mode for a script(公式)
既定値とグローバルの扱い
新規のスコープ付きアプリの既定は ES12
新規のグローバルアプリの既定は ES5 Standards
グローバルアプリは、アプリ全体では ES12 を選べない。ES12 を使うスクリプトだけ、スクリプト単位で有効にする
新規に作るスクリプトは、Xanadu 以降はアプリの設定に関係なく ES12 が既定
既存のスコープ付きアプリは、Tokyo 以降に ES12 で動かせる
今回の PDI のグローバルは、Compatibility Mode でした。グローバルの Scripts - Background もこのモードで実行され、globalThis が未定義になる動きと合っています。
ES Level の数字について
スクリプトのエラー出力には「Script ES Level」という数字が出ます。公式ドキュメントにはこの数字の説明が見つからず、意味は公開情報から確認できていません。今回の実測では、グローバルの Scripts - Background が 0、ES12 のスコープが 250 でした。モードの違いを示す目安として使えますが、数字の定義は推測を避けて「実測値」として扱います。
Brazil での変更点
Australia から Brazil への差分ノートには、次の2項目が載っています。
ECMAScript 2021 (ES12) JavaScript mode supports additional scripting features — Use additional scripting features in applications or scripts that use the ECMAScript 2021 (ES12) JavaScript mode.
(訳:ES12 モードがさらに多くのスクリプト機能をサポートする。ES12 モードを使うアプリやスクリプトで追加の機能が使える)
JavaScript engine updated with changes from the Rhino engine — The JavaScript engine on the ServiceNow AI Platform was updated to incorporate changes from the open-source Rhino JavaScript engine.
(訳:JavaScript エンジンが、オープンソースの Rhino エンジンの変更を取り込んで更新された)
引用元:Brazil 差分ノート(公式)
この2項目は、同じページの Australia の行にも Brazil の行にも、まったく同じ文面で載っています。ひとつ前の差分ノート(Zurich から Australia)の Zurich の行は、「including Promises and Async await」と具体名を挙げていました。Australia と Brazil の行には具体名が無いため、リリースノートだけでは Brazil の追加分を特定できません。
機能対応表と Rhino の取り込み一覧は、Australia 版しか読めませんでした。以下の公式の記述は Australia 版の内容です。
取り込み一覧には、Promise.try、Promise.withResolvers、Error.isError、新しい Set メソッド、TypedArray.from / of、String.prototype.replaceAll などの書き換えが載っている
修正としては、apply と call の this、メソッドをコンストラクタとして使えなくする変更、オブジェクトリテラルの重複キー、hoisting が載っている
機能対応表では、WeakRef と FinalizationRegistry は Disallowed(エラーになる)。private なインスタンスメソッドと、private なインスタンスフィールドの basic support は ES12 モードの列で Not Supported。公式は Not Supported を「現在のリリースで検証されていない」と定義している
引用元:JavaScript engine feature support(公式) / Updates to the JavaScript engine(公式)
この PDI のアプリのモード分布
コードがどのモードで動くかは、そのコードを持つアプリの設定で決まります。アプリの一覧を JavaScript Mode でグループ化すると、全体の分布が分かります。

①のグループは4つで、②の合計は788件です。内訳を文字でも書き出します。
ECMAScript 2021(ES12):564件
ES5 Standards Mode:192件
Compatibility Mode:17件
未設定(空):15件
この一覧は、ストアアプリやプラットフォームが同梱するアプリを含みます。グローバルは Compatibility Mode です。ES12 のアプリが7割を超えていても、自分が書くグローバルのスクリプトには関係がありません。
次の画像は、ES12 のアプリのレコードです。ストアアプリの1つを読み取り専用で開きました。

①のフィールドがアプリ単位のモードです。②のセクションには、次の注意書きがあります。
Changing these options may affect how your application runs once it's been installed on a target instance.
(訳:これらのオプションを変更すると、対象インスタンスにインストールしたあとのアプリの動きに影響することがある)
検証したスニペットと条件
試した条件は次のとおりです。
実行場所:JavaScript Mode を ECMAScript 2021 (ES12) にした一時のスコープ付きアプリと、グローバル(Compatibility Mode)
実行方法:Scripts - Background で、1機能につき1スクリプトを両方の場所で実行
対象:Australia 版の公式ページに載っている機能のうち、サーバスクリプトで使いやすいもの
判定:値が返れば「使えた」、コンパイルでエラーなら「構文エラー」、実行でエラーなら「実行エラー」
スコープ付きアプリの中では eval() が禁止されているため、構文ごとに別のスクリプトを実行しました。構文エラーが1つあるとスクリプト全体が実行されないためです。

表の内容を文字でも書き出します。
オプショナルチェーン ?. / Null 合体 ?? / ??=:ES12 で使えた、グローバルは構文エラー
||= / 数値区切り 1_000_000 / replaceAll / Array.at():両方で使えた
globalThis:ES12 は object、グローバルは未定義
BigInt リテラル:ES12 で使えた、グローバルは構文エラー
BigInt の **:ES12 でも実行エラー(Cannot convert BigInt to a number)
クラスのパブリックフィールド / static 初期化ブロック / private メソッド / private フィールド:ES12 で使えた、グローバルは構文エラー
Promise.allSettled / Promise.withResolvers:ES12 では関数として定義されていた(呼び出しは未実行)、グローバルは Promise が未定義
await:ES12 で実行できた、グローバルは構文エラー
async function / Promise.try:ES12 では関数として定義されていた(呼び出しは未実行)、グローバルは構文エラー
Set.prototype.union / TypedArray.from:ES12 で使えた、グローバルは Set と Uint8Array が未定義
WeakRef:両方で未定義
Error.isError:両方で使えた
動かなかった機能と注意点
BigInt の指数演算
2n * 3n は 6 を返しましたが、2n ** 10n は実行時エラーでした。公式の表では BigInt の基本機能は Supported です。基本機能が Supported でも、演算子ごとに動くとは限りません。指数演算を使う処理は、実行して確かめてから採用してください。
WeakRef
typeof WeakRef は undefined でした。公式の Disallowed の記載と一致します。メモリを弱参照で持つ設計は、サーバスクリプトでは使えません。
公式の表と違う動き
Australia 版の表では、private なインスタンスメソッドと private なインスタンスフィールドの basic support が、ES12 モードの列で Not Supported です。公式は Not Supported を「検証されていない」と定義しています。この Brazil PDI では、基本的な #p () と #f = 8 が動きました。Australia との実機比較はしておらず、表の他の行は簡単なスニペットでは確かめていません。
構文を使う前の確認
実行モードの確認
最初に、そのスクリプトを持つアプリの JavaScript Mode を調べます。スコープ付きアプリなら sys_app.list のレコード、グローバルならスクリプト単位の設定が手がかりです。モードが分からないまま ?. や class を書くと、実行して初めてコンパイルエラーに気づきます。
グローバルの扱い
グローバルのスクリプトは、アプリ全体を ES12 にできません。今回の PDI でも Compatibility Mode でした。使いたい構文があるスクリプトだけ、フォームの「Turn on ECMAScript 2021 (ES12) mode」で切り替えます。この切り替えは今回試していません。
モード切り替えで変わる動き
公式は、アプリやスクリプトのモードを切り替えると既存スクリプトの動きが変わりうると説明しています。例として挙がっているのは、次のとおりです。以下はすべて公式の記述で、今回は動かしていません。
arguments オブジェクトは、ES5 以降のモードで、パラメータの変更が反映されない
セミコロンの欠落は、Compatibility では自動挿入されるが、ES5 以降では構文エラーになる
let と yield は、ES12 ではキーワードになり、識別子に使うと構文エラーになる
存在しない関数の呼び出しは、ES12 で EcmaError になる
数値リテラルは、ES12 で二進数、八進数、BigInt が加わる
切り替える前に、テスト用のインスタンスで既存スクリプトを一通り動かします。
避けたほうがよいもの
BigInt の **:実行時エラー
WeakRef:未定義。公式でも Disallowed
公式の表で Not Supported の項目:スニペットが動いても、細かい条件で動かない可能性が残るため、本番のスクリプトには入れない
Rhino の取り込みによる動きの変更(this の扱い、メソッドをコンストラクタにする書き方、hoisting)に依存する古いコード:切り替え後にテストで確かめる
判断の順番
使いたい構文があるときは、次の順で見ていくと迷いません。
そのスクリプトはスコープ付きアプリの中か、グローバルか
スコープ付きなら、アプリの JavaScript Mode は ES12 か
グローバルなら、使いたいスクリプトで ES12 の切り替えを有効にできるか
使いたい構文は、公式の機能対応表で Supported か
対応表で Supported でも、対象のスコープで1回動かして値が返るか
5つ目まで進んだ構文だけを、本番のスクリプトに入れます。
自分のインスタンスで確かめる方法
一覧画面で確かめる
次の URL を自分のインスタンスの先頭に付けて開きます。
アプリを JavaScript Mode ごとにグループ化:/sys_scope_list.do?sysparm_query=GROUPBYjs_level
ES12 のアプリだけ:/sys_scope_list.do?sysparm_query=js_level=es_latest
ES5 Standards のアプリだけ:/sys_scope_list.do?sysparm_query=js_level=helsinki_es5
Compatibility のアプリだけ:/sys_scope_list.do?sysparm_query=js_level=traditional
モードが未設定のアプリ:/sys_scope_list.do?sysparm_query=js_levelISEMPTY
1件のアプリの設定:/sys_app.do?sys_id=<アプリの sys_id>(ストアアプリは /sys_store_app.do)
読み取り専用のスクリプトで数える
Scripts - Background に次を貼ると、モードごとの件数を数えます。レコードは変更しません。
var ga = new GlideAggregate('sys_scope');
ga.addAggregate('COUNT');
ga.groupBy('js_level');
ga.query();
while (ga.next()) {
gs.print((ga.getDisplayValue('js_level') || '(empty)') + ' : ' + ga.getAggregate('COUNT'));
}この PDI での出力は次のとおりです。
(empty) : 15
ECMAScript 2021 (ES12) : 564
ES5 Standards Mode : 192
Compatibility Mode : 17
特定のアプリのモードを知りたいときは、ga.addQuery('scope', '<スコープ名>') を足します。
グローバルでは同じ書き方が通らない
グローバルの Scripts - Background で user?.profile?.name を書くと、実行前にコンパイルエラーになりました。

①の Script ES Level は 0 で、ES12 スコープの実行では 250 と表示されました。②の行が原因です。ES12 モードで書いたコードを、グローバルの Scripts - Background にそのまま貼ると動きません。公式は Xanadu 以降、新規スクリプトの既定を ES12 としているため、新しく作ったグローバルのスクリプトでは別の結果になる可能性があります(未確認)。
実機で気づいたこと
グローバルで動く構文
グローバルの Scripts - Background は ES Level 0 でも、||= や数値区切りは動きました。replaceAll、Array.at()、Object.hasOwn、Error.isError も同じです。公式の表では、ES5 Standards モードでこれらの多くが Not Supported です。グローバルの Compatibility Mode は表の列に無く、実際の動きは表から読めません。「ES12 モードでないと使えない」と決め打ちせず、使いたい構文を対象のスコープで1回動かして確かめるのが確実です。
スコープ付きアプリの eval
スコープ付きアプリでは eval() が使えないため、構文の対応をスクリプト内でまとめて試す方法は取れません。構文を試すときは、1つの構文につき1回の実行になります。
差分ノートの粒度
Australia と Brazil に同じ文面が載るため、リリースノートは「このリリースで何が増えたか」を調べる材料になりません。確認したい構文は、自分のインスタンスで動かすのが早い方法です。
検証範囲とおことわり
Brazil 版の JavaScript engine のページは読めず、公式の記述は Australia 版(最終更新 2026-03-12)の内容です
Australia のインスタンスは無く、Brazil で新しく増えた機能かどうかは比べていません
ES Level 0 を確認したのは、グローバルの Scripts - Background だけです。新規に作るグローバルのスクリプトの既定は確かめていません
Promise.allSettled、Promise.withResolvers、Promise.try、async function は、関数として定義されていることだけを確認し、呼び出しは試していません
1機能につき1つの簡単なスニペットで、エッジケースは確かめていません
グローバルのスクリプト単位の ES12 切り替えは試していません
モード切り替えで動きが変わる例は、公式の記述の引用で、実機では動かしていません
検証用に一時のスコープ付きアプリを作り、実行後に削除しました。削除は確認済みです
用語
ES12:ECMAScript 2021。ServiceNow のモード名では「ECMAScript 2021 (ES12)」
Compatibility Mode:古いエンジンの挙動を保つモード
スコープ付きアプリ:独自のスコープ名を持つアプリ。グローバルは、スコープを持たない共通の領域
Scripts - Background:サーバ側のスクリプトを管理画面から実行する機能
調査で使ったURL
JavaScript モード:JavaScript modes(公式)
JavaScript エンジンの概要:JavaScript engine on the platform(公式)
スクリプトごとの ES12 の有効化:Turn on ES12 mode for a script(公式)
モード切り替えの注意:Considerations for switching JavaScript modes(公式)
Rhino の取り込み一覧:Updates to the JavaScript engine(公式)
Australia から Brazil への差分ノート:Brazil 差分ノート(公式)
関連記事は次の3本です。