JavaScript

JavaScriptのObject.seal():値を更新しながらオブジェクトの項目を固定する

この記事でわかること

Object.seal()でオブジェクトの項目を固定し、既存の値だけ更新する方法を解説。引数・戻り値、ブラウザーとNode.jsの実行手順、削除時の例外、入れ子への制約を学べます。

目的:処理の途中で必要な項目が消える事故を防ぐ

進捗情報を複数の関数で共有するとき、件数や状態は更新できても、必要な項目が削除されると困ります。例えば、集計処理が参照するcompletedが途中で消えると、計算結果が意図しない値になる可能性があります。

Object.seal()は、オブジェクトの項目構成を固定する静的メソッドです。書き込み可能な既存プロパティの値は更新できるため、項目が決まっている進捗データなどに向きます。

この記事では、進捗情報の削除を防ぐ例を通して、更新できる操作と失敗する操作を確認します。追加だけを禁止するObject.preventExtensions()よりも、既存項目を維持したい場面に適した選択です。

動作環境と対応バージョン

確認日は2026年10月27日です。MDNではObject.seal()を広く利用可能な機能とし、2015年7月以降、主要ブラウザーで横断的に利用できると説明しています。現在のChrome、Edge、Firefox、Safariで利用できます。MDNの仕様説明

ブラウザー専用のAPIではなく、Node.jsでも動作します。本記事の実行対象は現行ブラウザーとNode.js 22以降とします。このNode.jsの指定は記事の対象環境であり、APIが初めて実装されたバージョンを示すものではありません。

サンプルは外部ライブラリ、DOM、通信を使いません。両環境で同じJavaScriptを使用できます。古い組み込みブラウザーなどへ導入する場合は、対象環境でtypeof Object.seal === "function"となることも確認してください。

引数と戻り値:元のオブジェクト自身を変更する

基本の呼び出し方

構文はObject.seal(obj)です。引数objには、項目構成を固定したいオブジェクトを渡します。戻り値は引数と同じオブジェクトで、独立したコピーは作りません。

封印すると、自身の既存プロパティはconfigurable: falseになります。新しいプロパティの追加、既存プロパティの削除、列挙可能かどうかの変更などができなくなります。一方、既存のデータプロパティが書き込み可能なら、その値は引き続き更新できます。MDNの引数・戻り値と動作説明

したがって、封印する前に必要な項目を用意することが大切です。後から値が決まる項目も、初期値を入れておきます。例えば完了時刻ならfinishedAt: nullとしておき、処理終了時に日時へ更新する設計が考えられます。

コピーや値の検証は行わない

同じオブジェクトを参照する別の変数にも封印の効果が及びます。共有先への影響を避けたい場合は、共有する前の生成段階で適用する方が意図を説明しやすくなります。

また、封印は値の妥当性を保証しません。件数に文字列を代入しても、そのプロパティが書き込み可能なら更新できます。数値の範囲や状態名の検証は、更新用の関数で別途行ってください。

コピーして試す:更新・削除・追加を比較する

自己完結したサンプル

次のコード全体を使います。関数の先頭でstrict modeを有効にしており、削除と追加の失敗をそれぞれ捕捉します。失敗を確認した後も、残りの処理へ進める構成です。

JavaScript
(function runSealDemo() {
  "use strict";

  const progress = {
    total: 3,
    completed: 0,
    status: "waiting",
    details: { owner: "A" }
  };

  const sealed = Object.seal(progress);
  console.log(`same object: ${sealed === progress}`);

  progress.completed = 1;
  progress.status = "running";
  console.log(`progress: ${progress.completed}/${progress.total}`);
  console.log(`status: ${progress.status}`);

  try {
    delete progress.completed;
  } catch (error) {
    if (!(error instanceof TypeError)) throw error;
    console.log(`delete: ${error.name}`);
  }

  try {
    progress.startedAt = "2026-10-27";
  } catch (error) {
    if (!(error instanceof TypeError)) throw error;
    console.log(`add: ${error.name}`);
  }

  console.log(`completed remains: ${progress.completed}`);
  console.log(`startedAt exists: ${"startedAt" in progress}`);

  progress.details.note = "checked";
  console.log(`nested note: ${progress.details.note}`);
})();

最後のnoteの追加は成功します。これは、外側の封印が内側のオブジェクトへ自動的に適用されないことを示すための処理です。

ブラウザーでの実行方法

通常のWebページを開き、開発者ツールのConsoleを表示します。上のJavaScript全体を入力して実行してください。HTMLファイルの作成やサーバーの起動は不要です。

関数内に変数を閉じ込めているので、同じコードを繰り返して実行しても、トップレベルのconstの再宣言を避けられます。Consoleに評価結果としてundefinedが追加表示されることがありますが、ログの期待出力とは別です。

Node.jsでの実行方法

上のJavaScript全体をUTF-8のseal-demo.cjsとして保存します。ターミナルでそのファイルを保存したディレクトリへ移動し、次のコマンドを実行します。

Shell
node seal-demo.cjs

追加パッケージのインストールは不要です。ファイル名をnodeコマンドに渡して実行する方法は、Node.js公式の実行手順でも説明されています。

期待出力

両環境で、ログは次の順序になる想定です。これは公式仕様に基づく期待出力であり、この記事の作成時に実機実行した結果ではありません。

CODE
same object: true
progress: 1/3
status: running
delete: TypeError
add: TypeError
completed remains: 1
startedAt exists: false
nested note: checked

エラーメッセージ全文は実装によって異なるため、コードでは例外のnameだけを表示しています。非同期処理は含まれず、awaitやPromiseの待機も必要ありません。

失敗例と制約を理解する

strict modeがないと失敗を見落とすことがある

通常モードでは、封印済みオブジェクトへの代入による追加が、例外を出さずに失敗する場合があります。処理が止まらないため、追加できたと思い込む原因になります。

サンプルの"use strict"は、こうした失敗を例外として確認するために置いています。JavaScriptのモジュールは自動的にstrict modeになりますが、通常のスクリプトや実行環境との差を避けるため、例では関数内に明記しました。MDNのstrict mode解説

実務では、予期しない例外まで握りつぶさないことも大切です。例のように確認対象の例外を判別するか、呼び出し元へ伝えて原因を調べられるようにしてください。

入れ子は別に考える

サンプルのdetailsは別のオブジェクトです。内側の項目も固定したいなら、内側へ個別にObject.seal()を適用する必要があります。ただし外側のdetails自体が書き込み可能なら、別のオブジェクトへの差し替えは可能です。

このため、封印だけでデータ全体の構造を保証しようとすると設計が複雑になります。まずは平坦で項目数の少ないデータに適用し、入れ子の検証は更新処理の責務として整理すると扱いやすくなります。

値を固定する用途との違い

値の変更まで抑えたい用途では、Object.freeze()が候補になります。ただし、凍結も入れ子のオブジェクトへ自動的には及びません。必要な保護範囲を決めて選択してください。MDNの凍結と浅い制約の説明

また、封印したオブジェクトを展開構文などで新しく作り直すと、生成したオブジェクトへ封印状態が自動的に引き継がれるわけではありません。コピー後も同じ制約が必要なら、新しいオブジェクトに対して適用します。

活用事例と導入時の改善案

向いているのは、項目が事前に確定し、値だけが変化する小さなデータです。

  • バッチ処理の総件数、完了件数、処理状態を保持する進捗情報。
  • 読み込み中、成功件数、失敗件数をまとめた処理結果の集計用データ。
  • 必須項目の削除を防ぎながら値を更新する、内部処理向けの共有データ。

項目名を自由に増減させたい辞書には向きません。将来の追加が必要なら、必要な項目をそろえた新しいオブジェクトを生成する流れを検討します。

改善案として、封印と更新用関数を組み合わせる方法があります。生成時に項目構成を固定し、更新用関数では件数が総件数を超えないかなどを検証します。構造上の事故と業務上の不正値を別々に扱えるため、障害の原因を追いやすくなります。

まずはサンプルの削除処理を、自分のコードで起こり得る操作に置き換えてください。必要な項目を守りつつ、許可した更新が成功するかを確認するところから導入できます。

公式参考資料

PREFERRED SOURCES

Googleの優先するニュース提供元にSEの部屋を追加

Googleで、いつも読みたい情報源を選べます。登録可否はGoogleの画面で確認できます。

Googleの設定画面で確認する 新しいタブで開きます

候補にSEの部屋が表示されない場合は、まだ追加できません。

y.
WRITTEN BY

y_ymo10

SEの部屋で、HTML・CSSからJavaScript・React・Next.jsまで、Web制作の開発ノートを公開しています。

ほかの開発ノートを読む →