Skip to content

Instantly share code, notes, and snippets.

@think49
Last active April 10, 2018 12:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save think49/2aa2f7a19ddc3f2030dd288839c29f2b to your computer and use it in GitHub Desktop.
Save think49/2aa2f7a19ddc3f2030dd288839c29f2b to your computer and use it in GitHub Desktop.
deep-freeze-from-object.js: オブジェクトのプロパティを含めて、Object.freeze

deep-freeze-from-object.js

概要

ECMAScript 5 規定の Object.freeze() は引数のオブジェクト自身を不変にしますが、当該オブジェクトのプロパティに存在するオブジェクトは不変にせず、[[Prototype]] 上のオブジェクトも不変にしません。 deepFreezeFromObject() は次のオブジェクトを不変にします。

  1. 第一引数のオブジェクト
  2. 第一引数のオブジェクトが持つ enumerable(列挙可能)な直属のプロパティ配下のオブジェクト(第二引数がtruthyなら列挙不可能なプロパティも拾う)
    1. で得たオブジェクトを対象に 2. の処理を行い、プロパティに非Object型以外が存在するまで延々とどう処理を続ける

使い方

deepFreezeFromObject( object [, nonEnumerable ] )

第一引数に指定されたオブジェクトをプロパティを含めて不変にし、不変となったオブジェうとを返します。

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

リンク

/**
* deep-freeze-from-object-1.0.0.js
* Freeze the deep objects in the object.
*
* @version 1.0.0
* @author think49
* @url https://gist.github.com/think49/2aa2f7a19ddc3f2030dd288839c29f2b
* @license http://www.opensource.org/licenses/mit-license.php (The MIT License)
*/
if (typeof Object.freeze === 'function') {
var deepFreezeFromObject = (function (Object, freeze, getOwnKeys, getOwnPropertyNames) {
return function deepFreezeFromObject (object /* [, nonEnumerable ] */) {
if (Object(object) === object) {
freeze(object);
var keys = !arguments[1] ? getOwnKeys(object) : getOwnPropertyNames(object);
for (var i = 0, len = keys.length; i < len; ++i) {
deepFreezeFromObject(object[keys[i]]);
}
}
return object;
};
}(Object, Object.freeze, Object.keys, Object.getOwnPropertyNames));
}
<!DOCTYPE html>
<head>
<meta charset="UTF-8" />
<title>test</title>
<style>
</style>
</head>
<body>
<script src="deep-freeze-from-object-1.0.0.js"></script>
<script>
'use strict';
var object1 = {a:{b:true}};
var object2 = deepFreezeFromObject(object1);
console.assert(object1 === object2); // true
object1.a.b = false; // TypeError: Cannot assign to read only property 'b' of object '#<Object>'
</script>
<script>
'use strict';
deepFreezeFromObject({a:{b:{c:{d:true}}}}).a.b.c.d = false; // TypeError: Cannot assign to read only property 'd' of object '#<Object>'
</script>
<script>
'use strict';
var object = Object.create(null, {a:{enumerable: false, value: {}}});
deepFreezeFromObject(object); // 第二引数省略時、列挙可能なプロパティのみ不変にする
object.a.b = 1; // エラーにならない
console.assert(JSON.stringify(object.a) === '{"b":1}'); // true{"b":1}
</script>
<script>
'use strict';
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
</script>
<script>
'use strict';
var object = Object.create({b: 2}, {a:{enumerable: true, value: 1}});
deepFreezeFromObject(object, true);
Object.getPrototypeOf(object).b = 1; // [[Prototype]] 上のオブジェクトは不変ではない
console.assert(object.b === 1); // true
object.b = 1; // TypeError: Cannot add property b, object is not extensible
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment