MENU

JavaScript関数・メソッド

【JavaScript】 オブジェクトのプロパティ追加を禁止する方法

更新日:2021/09/01

 

自作のオブジェクトを共有ライブラリとして外部に公開するときなど、他者にオブジェクトを変更してほしくないときもあります。

 

タイプミスが頻繁で、予定にない新規プロパティが増えていて困るという人(僕)もいます。

 

そんなとき、Object.freeze()またはObject.seal()またはObject.preventExtensions()を使用します。

 

オブジェクトにプロパティを追加させたくないケース

 

既存プロパティの変更を防ぐなら、Object.defineProperty()というメソッドを使用します。しかしプロパティの数が増えてくると、一つ一つ属性を与えていくのはめんどうです。普通に定義しておいて、後で一括で変更できると嬉しいですね。

 

それと、Object.defineProperty()を使っても、オブジェクトに新規でプロパティを追加させることを制限できません。

 

そこでObject.freeze()またはObject.seal()またはObject.preventExtensions()を使用すると、一括で属性を変更したり、オブジェクトのプロパティ追加を禁止したりできます。

 

メソッドプロパティ追加writable属性configurable属性目的
Object.freeze()禁止falsefalse完全凍結:

値の変更もできなくする

Object.seal()禁止変更なしfalseプロパティ値の変更はできる
Object.preventExtensions()禁止変更なし変更なしプロパティ追加禁止のみ

 

上の表を見てわかるように、この三つのメソッドはフリーな状態から禁止する方向に属性を変更します。

 

一度これらのメソッドを呼び出した後、解除する方法はありません。

 

一見不便なように感じますが、これらのメソッドはセキュリティ強化という目的で呼び出されれます。
解除する方法があるとその目的を達成することができないので、仕方がないのです。

Object.freeze()

 

Object.freeze()は、既存プロパティのwritableconfigurable属性をfalseに設定します。
さらに、オブジェクトのプロパティ追加を不可にするフラグをセットします。

 

これによりオブジェクトの新規プロパティの追加、および既存プロパティの値と属性の変更が禁止されます。

 

Object.freeze()は、オブジェクトの改変をプロパティが持っている値を含めて禁止したいときに使用します。

 

構文

 

Object.freeze(obj)

 

 

■引数

 

obj: 凍結させるオブジェクト

 

■戻り値

 

引数で指定したオブジェクト

 

■使用例

 


"use strict";

const obj ={
    prop1:100,
    prop2:200,
    prop3:{}
};
   // 現在の属性を列挙
Object.getOwnPropertyNames(obj).forEach(e=>{
    const od = Object.getOwnPropertyDescriptor(obj,e);
    console.log( e ,od );
    // prop1{ value: 100, writable: true, enumerable: true, configurable: true }
    // prop2{ value: 200, writable: true, enumerable: true, configurable: true }
    // prop3{ value: {}, writable: true, enumerable: true, configurable: true }
});

  // オブジェクトをフリーズ
Object.freeze(obj);

   // フリーズ後の属性を列挙 → writable・configurable属性がfalseに変更されている
Object.getOwnPropertyNames(obj).forEach(e=>{
    const od = Object.getOwnPropertyDescriptor(obj,e);
    console.log( e ,od );
    // prop1{ value: 100, writable: false, enumerable: true, configurable: false }
    // prop2{ value: 200, writable: false, enumerable: true, configurable: false }
    // prop3{ value: {}, writable: false, enumerable: true, configurable: false }
});

obj.prop1 = 500; //TypeError: "prop1" is read-only

 

■strictモードと非strictモードでの動作の差

 

strictモードでは、書き込み禁止されたプロパティを変更しようとすると、エラーがスローされます。
非strictモードではエラーがスローされず、そのまま続行されます。

 

 

Object.seal()

Object.seal()は、既存プロパティのconfigurable属性をfalseに設定します。
さらに、オブジェクトのプロパティ追加を不可にするフラグをセットします。

 

これによりオブジェクトの新規プロパティの追加、および既存プロパティの属性変更が禁止されます。

 

Object.seal()は、オブジェクトの構造改変を禁止したいときに使用します。

 

構文

 

Object.seal(obj)

 

 

■引数

 

obj: シールさせるオブジェクト

 

■戻り値

 

引数で指定したオブジェクト

 

■使用例

 


"use strict";

const obj ={
    prop1:100,
    prop2:200,
    prop3:{}
};
   // 現在の属性を列挙
Object.getOwnPropertyNames(obj).forEach(e=>{
    const od = Object.getOwnPropertyDescriptor(obj,e);
    console.log( e ,od );
    // prop1{ value: 100, writable: true, enumerable: true, configurable: true }
    // prop2{ value: 200, writable: true, enumerable: true, configurable: true }
    // prop3{ value: {}, writable: true, enumerable: true, configurable: true }
});

  // オブジェクトをシール
Object.seal(obj);

   // シール後の属性を列挙 → configurable属性がfalseに変更されている
Object.getOwnPropertyNames(obj).forEach(e=>{
    const od = Object.getOwnPropertyDescriptor(obj,e);
    console.log( e ,od );
    // prop1{ value: 100, writable: true, enumerable: true, configurable: false }
    // prop2{ value: 200, writable: true, enumerable: true, configurable: false }
    // prop3{ value: {}, writable: true, enumerable: true, configurable: false }
});

obj.prop1 = 500; // writableがtrueのままなので、値を変更可能

 

Object.seal()を実行すると、obj直下への新規プロパティ追加と既存プロパティの削除がおこなえなくなります。

 

オブジェクトとその直下のプロパティ

 

obj{
 プロパティ
 プロパティ
 プロパティ
}

 

しかしプロパティ値の変更が禁止されていないので、オブジェクトを代入することで、オブジェクトの入れ子構造などの改変をおこなうことはできます。

 

入れ子構造などの改変

 

obj{
 プロパティ
 プロパティ
 プロパティ: ←代入 obj{
                   プロパティ
                   プロパティ
                }
}

 

Object.seal()実行前にObject.defineProperty()でwritable属性をfalseにセットしておくことで、一部のプロパティのみ上書き禁止することができます。

 


"use strict";

const obj ={
    prop1:100,
    // prop2:200,
    prop3:{}
};
Object.defineProperty(obj,"prop2",{
    value:200,
    writable:false , // 規定値
});

Object.getOwnPropertyNames(obj).forEach(e=>{
    const od = Object.getOwnPropertyDescriptor(obj,e);
    console.log( e ,od );
    // prop1{ value: 100, writable: true, enumerable: true, configurable: true }
    // prop3{ value: {}, writable: true, enumerable: true, configurable: true }
    // prop2{ value: 200, writable: false, enumerable: true, configurable: true }
});
Object.seal(obj);

Object.getOwnPropertyNames(obj).forEach(e=>{
    const od = Object.getOwnPropertyDescriptor(obj,e);
    console.log( e ,od );
    // prop1{ value: 100, writable: true, enumerable: true, configurable: false }
    // prop3{ value: {}, writable: true, enumerable: true, configurable: false }
    // prop2{ value: 200, writable: false, enumerable: true, configurable: false }
});

obj.prop2 = 500; //TypeError: "prop2" is read-only

Object.preventExtensions()

 

Object.preventExtensions()は、オブジェクトのプロパティ追加を不可にするフラグをセットします。
これにより、以降のプロパティ追加ができなくなります。

 

Object.preventExtensions()は、既存プロパティの属性はそのままで、オブジェクトのプロパティ追加を禁止したいとき使用します。

 

構文

 

Object.preventExtensions(obj)

 

 

■引数

 

obj: プロパティ追加を禁止するオブジェクト

 


"use strict";

const obj ={
    prop1:100,
    prop2:200,
    prop3:{}
};
// 現在の属性を列挙
Object.getOwnPropertyNames(obj).forEach(e=>{
    const od = Object.getOwnPropertyDescriptor(obj,e);
    console.log( e ,od );
    // prop1{ value: 100, writable: true, enumerable: true, configurable: true }
    // prop2{ value: 200, writable: true, enumerable: true, configurable: true }
    // prop3{ value: {}, writable: true, enumerable: true, configurable: true }
});

// オブジェクトのプロパティ追加を禁止
Object.preventExtensions(obj);

// 禁止後の属性を列挙 → 属性の変更なし
Object.getOwnPropertyNames(obj).forEach(e=>{
    const od = Object.getOwnPropertyDescriptor(obj,e);
    console.log( e ,od );
    // prop1{ value: 100, writable: true, enumerable: true, configurable: true }
    // prop2{ value: 200, writable: true, enumerable: true, configurable: true }
    // prop3{ value: {}, writable: true, enumerable: true, configurable: true }
});

obj.newProp = 100; // Uncaught TypeError: can't define property "newProp": Object is not extensible

 

属性変更の範囲

 

既存プロパティの属性に左右されない

 

Object.defineProperty()を使用すると、for-in文やObject.keys()での列挙許可(enumerable属性)や属性の変更許可(configurable属性)を設定できます。

 

Object.freeze()とObject.seal()はこれらの属性に左右されず、オブジェクトが直接所持しているプロパティ全ての属性を変更します。

 

入れ子構造は対象外

 

Object.freeze()とObject.seal()の対象となるのは、オブジェクトの直下のプロパティのみです。

 

オブジェクトのプロパティ中にオブジェクトが存在する入れ子状態のとき、入れ子となっているオブジェクトの属性は変更されません。
変更したいときは、個別にメソッドを実行する必要があります。

 


const obj ={
    prop1:100,
    prop3:{
        prop31:100,
        prop32:{
            prop321:100
        }
    }
};
Object.freeze(obj);
Object.freeze(obj.prop3);
Object.freeze(obj.prop3.prop32);

 

配列のフリーズ

 

配列はArrayオブジェクトのため、Object.freeze()とObject.seal()を使用できます。

 


const array =[1,2,3];

Object.freeze(array);
Object.getOwnPropertyNames(array).forEach(e=>{
    const od = Object.getOwnPropertyDescriptor(array,e);
    console.log( e ,od );
    // 0 { value: 1, writable: false, enumerable: true, configurable: false }
    // 1 { value: 2, writable: false, enumerable: true, configurable: false }
    // 2 { value: 3, writable: false, enumerable: true, configurable: false }
    // length { value: 3, writable: false, enumerable: false, configurable: false }
});
array[0]=100; // TypeError: 0 is read-only

 

ただし多次元配列は、オブジェクトの入れ子になります。

 

そのため配列全体を凍結するには、入れ子となっているオブジェクトにもメソッドを適用する必要があります。

 


const array =[ [10,11,12],[20,21,22],[30,31,32] ];

Object.freeze(array);

array[2][2]=100; // エラーにならない

for( let i = 0; i < array.length ; i++ ){
    Object.freeze(array[i]);
}

array[2][2]=100; // TypeError: 2 is read-only

 

変更されるのは参照先のオブジェクト

 

Object.freeze()やObject.seal()などの対象となるのは、参照先のオブジェクトということに注意してください。

 

次のコードは、変数objを凍結しているような印象を受けます。

 


const obj ={
    prop1:100,
};
Object.freeze(obj);
obj.prop1 = 1000; // 変更できない

 

しかし実際には変数objが参照する、オブジェクトを対象としています。
そのため次のようなコードに書き換えることができます。

 


const obj = Object.freeze({
    prop1:100,
});

 

ただし、上のコードは変数objをconstで定義しているため、変数objを変更することができません。

プロトタイプのフリーズ

 

コンストラクターのprototypeオブジェクトは、インスタンスのプロトタイプチェーンに組み込まれます。
プロトタイプチェーンへの不正な関数組み込みを防止するために、コンストラクターのprototypeオブジェクトをフリーズしておくのも有効な手段です。

 

まずはprototypeプロパティにセットされているオブジェクトをフリーズします。

 

prototypeプロパティにセットされているオブジェクトをフリーズ

 


const f = function(){ };
f.prototype = { func1 : function(){} };

Object.freeze( f.prototype );

f.prototype = 1; // 変更可能

 

上の例では、f.prototype.func1 の値を変更できません。

 

しかし、f.prototype の値は変更できてしまいます。

 


f.prototype = 1; // 変更可能

 

コンストラクターそのものをフリーズすることで、対応します。

 


Object.freeze(f.prototype);
Object.freeze(f);

 

けーちゃんおススメJavaScript入門書

  • スラスラ読める JavaScript ふりがなプログラミング
  • プログラム未経験者がJavaScript始めるならコレ!
    コードを掲載して自分で理解しろという投げっぱなしな入門書とは異なり、コードに一つ一つどんなことをやっているかをふりがなという形式で解説しています。
    それでいてJavaScriptの基礎と応用を学べる良書です。
  • これからWebをはじめる人のHTML&CSS、JavaScriptのきほんのきほん
  • JavaScriptの機能を実践で活かすにはHTMLやCSSの知識が不可欠です。
    しかしそれらの知識があることが前提として書かれている書籍が多い中、この本は総合的な知識を身に着けることができます。
    HTMLやCSSの知識も不安な方には、ぴったりの一冊です
  •  

    入門書の役割は、自分のやりたいことをネットで調べることができるようになるための、基礎的な知識の獲得です。
    まずはこれらの本でしっかりと基礎知識を身につけましょう。
    そしてもっと高度なことや専門的なことはネットで調べ、情報が足りないと感じたら書籍を購入してください。


    記事の内容について

     

    こんにちはけーちゃんです。
    説明するのって難しいですね。


    「なんか言ってることおかしくない?」
    たぶん、こんなご意見あると思います。

    裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。

    そんなときは、ご意見もらえたら嬉しいです。

    ご意見はこちら。
    https://note.affi-sapo-sv.com/info.php

    【お願い】

    お願い

    ■このページのURL


    ■このページのタイトル


    ■リンクタグ


    ※リンクして頂いた方でご希望者には貴サイトの紹介記事を作成してリンクを設置します。
    サイト上部の問い合わせよりご連絡ください。
    ただしサイトのジャンルによっては、お断りさせていただくことがあります。