JSにおける属性記述オブジェクト6

5701 ワード

制御対象の状態
JavaScriptは、オブジェクトの読み書き状態を正確に制御し、オブジェクトの変更を防ぐ3つの方法を提供します.最弱層の保護はObject.prevent Extensionsで、その次はObject.sealで、最強のObject.freezeです.
Object.prevent Extensions()
Object.prevent Extensions方法は、1つのオブジェクトが新しい属性を追加できなくなります.
var o = new Object();

Object.preventExtensions(o);

Object.defineProperty(o, 'p', {
  value: 'hello'
});
// TypeError: Cannot define property:p, object is not extensible.

o.p = 1;
o.p // undefined
厳格なモードであれば、エラーが発生します.
(function () {
  'use strict';
  o.p = '1'
}());
// TypeError: Can't add property bar, object is not extensible
ただし、prevent Extens方法を使用したオブジェクトに対しては、deleteコマンドで既存の属性を削除することができます.
var o = new Object();
o.p = 1;

Object.preventExtensions(o);

delete o.p;
o.p // undefined
Object.isExtensioble()
Object.isExtensioble方法は、オブジェクトがObject.prevent Extensions方法を使用しているかどうかを確認するために使用されます.つまり、オブジェクトに属性を追加できるかどうかをチェックします.
var o = new Object();

Object.isExtensible(o) // true
Object.preventExtensions(o);
Object.isExtensible(o) // false
上のコードは新たにoオブジェクトを生成し、オブジェクトにObject.isExtensioble方法を使用して、trueに戻り、新しい属性を追加できることを示しています.このオブジェクトに対してObject.prevent Extensions方法を使用した後、Object.isExtensioble方法を使用してfalseに戻り、新しい属性を追加できなくなったということです.
Object.seal()
Object.sealメソッドは、オブジェクトに新しい属性を追加することも、古い属性を削除することもできません.
var o = {
  p: 'hello'
};

Object.seal(o);

delete o.p;
o.p // "hello"

o.x = 'world';
o.x // undefined
上のコードでは、オブジェクトがObject.sealメソッドを実行した後、新しい属性を追加したり、古い属性を削除したりすることができません.
Object.sealは、実質的に属性記述オブジェクトのconfigurble属性をfalseとするので、属性記述オブジェクトは変更できなくなります.
var o = {
  p: 'a'
};

// seal    
Object.getOwnPropertyDescriptor(o, 'p')
// Object {
//   value: "a",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

Object.seal(o);

// seal    
Object.getOwnPropertyDescriptor(o, 'p')
// Object {
//   value: "a",
//   writable: true,
//   enumerable: true,
//   configurable: false
// }

Object.defineProperty(o, 'p', {
  enumerable: false
})
// TypeError: Cannot redefine property: p
上のコードでは、Object.sealメソッドを使うと、属性記述オブジェクトのconfigrable属性がfalseになり、enumerable属性を変更するとエラーが発生します.
書いてもいいです.ちょっと特別です.もしwritableがfalseであれば、Object.sealの方法を使ってから、それをtrueに変えることはできません.しかし、もしwritableがtrueであれば、依然としてfalseに変えることができます.
var o1 = Object.defineProperty({}, 'p', {
  writable: false
});
Object.seal(o1);
Object.defineProperty(o1, 'p', {
  writable:true
})
// Uncaught TypeError: Cannot redefine property: p

var o2 = Object.defineProperty({}, 'p', {
  writable: true
});
Object.seal(o2);
Object.defineProperty(o2, 'p', {
  writable:false
});

Object.getOwnPropertyDescriptor(o2, 'p')
// {
//   value: '',
//   writable: false,
//   enumerable: true,
//   configurable: false
// }
上のコードの中で、同じくObject.seal方法を使っています.もしwritableがfalseであるなら、この設定を変更するとエラーが発生します.trueであれば、問題はありません.
属性オブジェクトのvalueが変更できるかどうかは、writableが決定します.
var o = { p: 'a' };
Object.seal(o);
o.p = 'b';
o.p // 'b'
上のコードの中で、Object.sealメソッドがp属性のvalueに対して無効であるのは、このときp属性のwritableがtrueであるからです.
Object.isSealed()
Object.isSealed方法は、オブジェクトがObject.sealメソッドを使用しているかどうかを確認するために使用されます.
var o = { p: 'a' };

Object.seal(o);
Object.isSealed(o) // true
このとき、Object.isExtenseble方法もfalseに戻ります.
var o = { p: 'a' };

Object.seal(o);
Object.isExtensible(o) // false
Object.freeze()
Object.freezeメソッドは、オブジェクトに新しい属性を追加できなくなり、古い属性を削除できなくなり、属性の値を変更することができなくなり、このオブジェクトが実際に定数になります.
var o = {
  p: 'hello'
};

Object.freeze(o);

o.p = 'world';
o.p // hello

o.t = 'hello';
o.t // undefined
上のコードでは、既存の属性に対して値を再割り当て(o.p='world)したり、新しい属性を追加したりします.しかし、厳しいモードであれば、エラーが発生します.
var o = {
  p: 'hello'
};

Object.freeze(o);

//          
(function () {
  'use strict';
  o.p = 'world';
}())
// TypeError: Cannot assign to read only property 'p' of #

//         
(function () {
  'use strict';
  o.t = 123;
}())
// TypeError: Can't add property t, object is not extensible
Object.isFrozen()
Object.isFrozen法は、オブジェクトがObject.freeze()メソッドを使用しているかどうかを確認するために使用されます.
var obj = {
  p: 'hello'
};

Object.freeze(obj);
Object.isFrozen(obj) // true
前に述べましたが、オブジェクトが凍結されたら、その属性に値を付けます.厳しいモードでエラーが発生します.Object.isFrozen方法はこのようなエラーが発生するのを防ぐことができます.
var obj = {
  p: 'hello'
};

Object.freeze(obj);

if (!Object.isFrozen(obj)) {
  obj.p = 'world';
}
上のコードの中で、objが凍結されていないことを確認してから、その属性に対して値を付けたら、エラーが発生しません.
限界性
上記の方法では、オブジェクトの書き込み可能性に穴がありますが、元のオブジェクトを変更することにより、オブジェクトの属性を増加することができます.
var obj = new Object();
Object.preventExtensions(obj);

var proto = Object.getPrototypeOf(obj);
//      
proto.t = 'hello';
obj.t
// hello
一つの解決策は原型を凍結することです.
var obj = Object.seal(
  Object.create(
    Object.freeze({x: 1}),
    {
      y: {
        value: 2,
        writable: true
      }
    }
  )
);

Object.getPrototypeOf(obj).hello = "hello";
obj.hello // undefined
もう一つの限界は、属性値がオブジェクトである場合、上記の方法は属性が指すオブジェクトを凍結するだけで、オブジェクト自体の内容を凍結することができません.
var obj = {
  foo: 1,
  bar: ['a', 'b']
};
Object.freeze(obj);

obj.bar.push('c');
obj.bar // ["a", "b", "c"]
上のコードでは、Obj.bar属性は1つの配列を指しています.Objオブジェクトが凍結された後、この指向は変更できません.つまり、他の値を指すことはできませんが、指し示す配列は変更できます.