diff --git a/index.js b/index.js index 4f45b70..46e0cdd 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,22 @@ -module.exports = function deepFreeze (o) { - Object.freeze(o); +'use strict'; +module.exports = function deepFreeze (o, method) { + if (!method) { + var method = 'freeze'; + } + + function isNotExtensible(o) { + return !Object.isExtensible(o); + } + + var checkFn; + switch (method) { + case 'freeze': checkFn = Object.isFrozen; break; + case 'seal': checkFn = Object.isSealed; break; + case 'preventExtensions': checkFn = isNotExtensible; break; + default: throw new TypeError("Method must be 'freeze', 'seal', or 'preventExtensions'"); + } + + Object[method](o); var oIsFunction = typeof o === "function"; var hasOwnProp = Object.prototype.hasOwnProperty; @@ -9,8 +26,8 @@ module.exports = function deepFreeze (o) { && (oIsFunction ? prop !== 'caller' && prop !== 'callee' && prop !== 'arguments' : true ) && o[prop] !== null && (typeof o[prop] === "object" || typeof o[prop] === "function") - && !Object.isFrozen(o[prop])) { - deepFreeze(o[prop]); + && !checkFn(o[prop])) { + deepFreeze(o[prop], method); } }); diff --git a/readme.markdown b/readme.markdown index 378c6ed..cc8df39 100644 --- a/readme.markdown +++ b/readme.markdown @@ -42,6 +42,29 @@ var deepFreeze = require('deep-freeze-strict') Call `Object.freeze(obj)` recursively on all unfrozen properties of `obj` that are functions or objects. +## deepFreeze(obj, 'freeze') + +Same as `deepFreeze(obj)`. + +## deepFreeze(obj, 'seal') + +Call `Object.seal(obj)` recursively on all unsealed properties of `obj` that +are functions or objects. + +## deepFreeze(obj, 'preventExtensions') + +Call `Object.preventExtensions(obj)` recursively on all not isExtensible properties of `obj` that +are functions or objects. + +# Freeze, seal, and preventExtensions +`'preventExtensions'` is the least restrictive. It prevents arbitraty properties from added to an object. + +`'seal'` does everything preventExtensions does, and also prevents altering the attributes of the properties on the object, as well as preventing deletion of properties on the object. + +`'freeze'` is the most restrictive. It does everything preventExtensions and seal does, as well as making all of the existing properties on the object read-only. + +Once an object is frozen (or sealed, or not extensible), it cannot be undone. + # license public domain diff --git a/test/freeze.js b/test/freeze.js new file mode 100644 index 0000000..33f22d8 --- /dev/null +++ b/test/freeze.js @@ -0,0 +1,23 @@ +var test = require('tap').test; +var deepFreeze = require('../'); + +test('freeze', function (t) { + "use strict"; + + t.plan(6); + + var a = { + // a function + b: function() {}, + }; + + deepFreeze(a, 'freeze'); + t.assert(!Object.isExtensible(a)); + t.assert(!Object.isExtensible(a.b)); + + t.assert(Object.isSealed(a)); + t.assert(Object.isSealed(a.b)); + + t.assert(Object.isFrozen(a)); + t.assert(Object.isFrozen(a.b)); +}); \ No newline at end of file diff --git a/test/preventExtensions.js b/test/preventExtensions.js new file mode 100644 index 0000000..21fea82 --- /dev/null +++ b/test/preventExtensions.js @@ -0,0 +1,23 @@ +var test = require('tap').test; +var deepFreeze = require('../'); + +test('preventExtensions', function (t) { + "use strict"; + + t.plan(6); + + var a = { + // a function + b: function() {}, + }; + + deepFreeze(a, 'preventExtensions'); + t.assert(!Object.isExtensible(a)); + t.assert(!Object.isExtensible(a.b)); + + t.assert(!Object.isSealed(a)); + t.assert(Object.isSealed(a.b)); + + t.assert(!Object.isFrozen(a)); + t.assert(!Object.isFrozen(a.b)); +}); diff --git a/test/seal.js b/test/seal.js new file mode 100644 index 0000000..7b47787 --- /dev/null +++ b/test/seal.js @@ -0,0 +1,23 @@ +var test = require('tap').test; +var deepFreeze = require('../'); + +test('seal', function (t) { + "use strict"; + + t.plan(6); + + var a = { + // a function + b: function() {}, + }; + + deepFreeze(a, 'seal'); + t.assert(!Object.isExtensible(a)); + t.assert(!Object.isExtensible(a.b)); + + t.assert(Object.isSealed(a)); + t.assert(Object.isSealed(a.b)); + + t.assert(!Object.isFrozen(a)); + t.assert(!Object.isFrozen(a.b)); +}); \ No newline at end of file diff --git a/test/type.js b/test/type.js new file mode 100644 index 0000000..144ae06 --- /dev/null +++ b/test/type.js @@ -0,0 +1,44 @@ +var test = require('tap').test; +var deepFreeze = require('../'); + +/** + * Test freeze method (freeze, seal, or preventExtensions). + */ + +test('freeze', function (t) { + "use strict"; + + t.plan(4); + + var a = { + // a function + b: function() {}, + }; + + deepFreeze(a, 'freeze'); + t.assert(Object.isFrozen(a)); + t.assert(Object.isFrozen(a.b)); + try { + a.x = 5; + } catch (e) { + t.ok('error thrown as expected'); + } + t.equal(a.x, undefined); +}); + +test('bad type', function (t) { + "use strict"; + + t.plan(1); + + var a = { + // a function + b: function() {}, + }; + + try { + deepFreeze(a, 'whoops'); + } catch (e) { + t.ok('error thrown as expected'); + } +}); \ No newline at end of file