ECMAScript 5 規定の Object.freeze()
は引数のオブジェクト自身を不変にしますが、当該オブジェクトのプロパティに存在するオブジェクトは不変にせず、[[Prototype]]
上のオブジェクトも不変にしません。
deepFreezeFromObject()
は次のオブジェクトを不変にします。
- 第一引数のオブジェクト
- 第一引数のオブジェクトが持つ
enumerable
(列挙可能)な直属のプロパティ配下のオブジェクト(第二引数がtruthyなら列挙不可能なプロパティも拾う) -
- で得たオブジェクトを対象に 2. の処理を行い、プロパティに非Object型以外が存在するまで延々とどう処理を続ける
第一引数に指定されたオブジェクトをプロパティを含めて不変にし、不変となったオブジェうとを返します。
var object1 = {a:{b:true}};
var object2 = deepFreezeFromObject(object1);
console.log(object1 === object2); // true
object1.a.b = false; // TypeError: Cannot assign to read only property 'b' of object '#<Object>'
プロパティはObject型でなくなるまで再帰的に検索します。
deepFreezeFromObject({a:{b:{c:{d:true}}}}).a.b.c.d = false; // TypeError: Cannot assign to read only property 'd' of object '#<Object>'
第二引数が省略された場合、列挙不可能なプロパティに存在するオブジェクトのみを不変にします。
var object = Object.create(null, {a:{enumerable: false, value: {}}});
deepFreezeFromObject(object); // 第二引数省略時、列挙可能なプロパティのみ不変にする
object.a.b = 1; // エラーにならない
console.log(JSON.stringify(object.a)); // {"b":1}
第二引数がtruthyな場合、列挙可能及び列挙不可能なプロパティに存在するオブジェクトを不変にします。
var object = Object.create(null, {a:{enumerable: false, value: {}}});
deepFreezeFromObject(object, true); // 第二引数がtruthyの時、列挙可能/列挙不可能なプロパティ(要するに全て)を不変にする
object.a.b = 1; // TypeError: Cannot add property b, object is not extensible
[[Prototype]]
上のオブジェクトは不変になりませんが、単純なプロパティアクセス演算子による代入は直属のプロパティへの代入扱いされ、TypeError
となります。
var object = Object.create({b: 2}, {a:{enumerable: true, value: 1}});
deepFreezeFromObject(object, true);
Object.getPrototypeOf(object).b = 1; // [[Prototype]] 上のオブジェクトは不変ではない
console.log(object.b); // 1
object.b = 1; // TypeError: Cannot add property b, object is not extensible