uhyohyo.net

JavaScript初級者から中級者になろう

十一章第八回 凍結と封印

このページの最終更新日:

今回はES5で追加された、オブジェクトに関するいくつかのメソッドを紹介します。これらのメソッドは、オブジェクトに対する変更を制限することが主な目的です。

ご存知のように、JavaScriptにおけるオブジェクトは、自由にプロパティを追加したり書き換えたり削除したりできます。しかし、場合によっては他人に勝手にオブジェクトをいじれらたくないこともあるでしょう。

プロパティの属性のひとつであるwritable属性をfalseにすれば、個々のプロパティについては書き換え不能にすることができます。しかし、新しいプロパティを追加されるような事態に対しては対応不可能です。今回紹介するメソッドを使えばこれを制限することができます。

Object.preventExtensions

まず最初に紹介するのがObject.preventExtensionsです。これはオブジェクトを引数にとって呼び出すと、そのオブジェクトは拡張不可能になります。すなわち、新しいプロパティを追加できなくなります


var obj = {
  foo: 'Hi',
};

Object.preventExtensions(obj);

obj.bar = 3;
console.log(obj.bar); // undefined
console.log('bar' in obj); // false

この例では、オブジェクトobjに対してObject.preventExtensionsを適用しています。preventExtensionsの効果により、これ以降objに対して新しいプロパティを追加することはできません。そのため、上の例では7行目でbarプロパティに代入しているにも関わらず、それは無視され、その後8,9行目でbarプロパティの存在を確かめても存在しないという結果になっています。(なお、近々紹介する予定のstrictモードでは、拡張不可能なオブジェクトに対してこのような操作を行うとエラーになります。)

また、Object.definePropertyにより新しいプロパティを作ることもやはりできません。

ただし、オブジェクトを拡張不可能にしても、delete演算子によりプロパティを削除することはできます。また、既存のプロパティに対する操作は自由にできます。

また、オブジェクトが拡張可能かどうかを調べるメソッドObject.isExtensibleがあります。例えば、上で定義されたオブジェクトobjに対してObject.isExtensible(obj)はfalseとなります。

Object.preventExtensionsが呼ばれていないオブジェクトは拡張可能です。また、一度拡張不可能にされたオブジェクトは拡張可能に戻すことはできません。

Object.seal

次に紹介するのはObject.sealです。順番が前後しますが、これが封印ですね。

このメソッドは、オブジェクトを拡張不可能にするのに加えて全てのプロパティのconfigurable属性をfalseに設定します。以前に説明した通り、configurable属性がfalseとなったプロパティは、その後属性を変更したり、また削除したりすることができなくなります。これにより、オブジェクトの形(どのプロパティがあるかとか)を変更することができなくなります。

ただし、プロパティのwritable属性は変更されません。ということは、既存のプロパティは依然として変更可能であるということです。


var obj = {
  foo: 'Hi',
};

Object.seal(obj);

obj.foo = 'foo';
obj.bar = 3;
console.log(obj); // {foo: "foo"}

そして、オブジェクトが封印されているかどうかを確かめるObject.isSealedメソッドもあります。

ただし、封印されているかどうかの判定は、オブジェクトが拡張不可能で全てのプロパティのconfiguragble属性がfalseになっているかどうかの判定により行われます。したがって、上のpreventExtensionsメソッドを用いつつ手動でconfigurableをfalseにした場合でもObject.isSealedの結果がtrueになります。


var obj = {
  prop: 'Hi',
};

Object.preventExtensions(obj);

console.log(Object.isSealed(obj)); // false

// propのconfigurable属性をfalseに
Object.defineProperty(obj, 'prop', {
  value: 'Hi',
  writable: true,
  configurable: false,
});

console.log(Object.isSealed(obj)); // true

Object.freeze

最後にObject.freezeです。これが凍結にあたります。

このメソッドもやはり与えられたオブジェクトを拡張不可能にします。さらに、既存の全てのプロパティのconfigurable属性及びwritable属性をfalseにします。

要するに、Object.sealの機能に加えて各プロパティのwritable属性もfalseにするということです。これにより、与えられたオブジェクトはプロパティが固定されたオブジェクトとなります。すなわち、プロパティが増えたり減ったりしないし、既存のプロパティも変わることはありません。さらにconfigurable属性もfalseになっているため設定を変更してプロパティを無理やり書き換えるようなこともできません。

そして、オブジェクトが凍結状態になっているかどうか判定するメソッドがObject.isFrozenです。


var obj = {
  prop: 'Hi',
};

Object.freeze(obj);

obj.prop = '吉野家';

console.log(obj.prop); // "Hi"

console.log(Object.isFrozen(obj)); // true

結局、今回全く新しく出てきた概念はオブジェクトが拡張可能かどうか、そしてそれを操作するObject.preventExtensionsメソッドです。Object.sealとObject.freezeはそれとプロパティの属性を組み合わせた便利メソッドというわけです。