Protecting objects in JavaScript

[2013-08-25] dev, javascript, jslang
(Ad, please don’t block)
This blog post is a quick refresher of how objects can be protected in JavaScript. There are three levels of protection:
  1. Preventing extensions is the weakest level,
  2. sealing is stronger,
  3. freezing is strongest.

Preventing extension

    Object.preventExtensions(obj)
makes it impossible to add properties to obj. Example:
    var obj = { foo: 'a' };
    Object.preventExtensions(obj);
Now adding a propert fails silently in sloppy mode:
    > obj.bar = 'b';
    > obj.bar
    undefined
And throws an error in strict mode [1], which we switch to via an IIFE [2].
    > (function () { 'use strict'; obj.bar = 'b' }());
    TypeError: Can't add property bar, object is not extensible
You can still delete properties, though.
    > delete obj.foo
    true
    > obj.foo
    undefined

Checking whether an object is extensible

    Object.isExtensible(obj)
checks whether obj is extensible:
    > Object.isExtensible(obj)
    false

Sealing

    Object.seal(obj)
prevents extensions and makes all properties “unconfigurable”. The latter means that the attributes [3] of properties can’t be changed, any more. Read-only properties stay read-only, enumerable properties stay enumerable, etc.

(As an aside, JavaScript does allow you to change an unconfigurable property from writable to read-only, due to historical reasons.)

The following example demonstrates that sealing makes all properties unconfigurable.

    > var obj = { foo: 'a' };

    > Object.getOwnPropertyDescriptor(obj, 'foo')  // before sealing
    { value: 'a',
      writable: true,
      enumerable: true,
      configurable: true }

    > Object.seal(obj)

    > Object.getOwnPropertyDescriptor(obj, 'foo')  // after sealing
    { value: 'a',
      writable: true,
      enumerable: true,
      configurable: false }
You can still change the property foo:
    > obj.foo = 'b';
    'b'
    > obj.foo
    'b'
But you can’t change its attributes:
    > Object.defineProperty(obj, 'foo', { enumerable: false });
    TypeError: Cannot redefine property: foo
Additionally, obj is not extensible, any more.

Checking whether an object is sealed

    Object.isSealed(obj)
checks whether obj is sealed:
    > Object.isSealed(obj)
    true

Freezing

    Object.freeze(obj)
makes all properties non-writable and seals obj. That is, obj is not extensible, all properties are read-only and there is no way to change that.
    var point = { x: 17, y: -5 };
    Object.freeze(point);
Once again, you get silent failures in sloppy mode:
    > point.x = 2;  // no effect, point.x is read-only
    > point.x
    17

    > point.z = 123;  // no effect, point is not extensible
    > point
    { x: 17, y: -5 }
And errors in strict mode:
    > (function () { 'use strict'; point.x = 2 }());
    TypeError: Cannot assign to read-only property 'x'

    > (function () { 'use strict'; point.z = 123 }());
    TypeError: Can't add property z, object is not extensible

Checking whether an object is frozen

    Object.isFrozen(obj)
checks whether obj is frozen:
    > Object.isFrozen(point)
    true

References

  1. JavaScript’s strict mode: a summary
  2. JavaScript variable scoping and its pitfalls
  3. Object properties in JavaScript