Why use Object.freeze and why not to use Object.freeze

This is an update to an original post I wrote Why use Object.freeze.

Two things which need to be made clear with the usage of Object.freeze are:

  1. Using Object.freeze can have a considerable impact on the execution time of your application (which will be demonstrated in this update)
  2. It only freezes the top level properties of the object graph - which is not immediately obvious when you read some of the documentation out there.

The Object.freeze() method freezes an object: that is, prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object is made effectively immutable. The method returns the object being frozen.

source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze

Execution time

Simple Object

The following examples use a simple iteration to create an object. The first one does nothing special and simply creates and returns an object.

function createObject() {  
    return {
        a: 1,
        b: 2
    };
}

for (var i = 0; i < 1000000; i++) {  
    createObject();
}

The timings are:

real    0m0.030s  
user    0m0.018s  
sys     0m0.010s  

Frozen Object

If the above is altered so that it freezes the object, the difference in execution time is enormous.

function createFrozenObject() {  
    return Object.freeze({
        a: 1,
        b: 2
    });
}

for (var i = 0; i < 1000000; i++) {  
    createFrozenObject();
}

The timings are:

real    0m2.971s  
user    0m1.495s  
sys     0m1.290s  

Deep Freeze Object

To address point two at the start of this update and also to show a further reduction in performance, this example will use a function called deepFreeze from the Mozilla Developer Network.

Part way down in the documentation it does state this behaviour with Object.freeze.

Values cannot be changed for data properties. Accessor properties (getters and setters) work the same (and still give the illusion that you are changing the value). Note that values that are objects can still be modified, unless they are also frozen.

source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze

function deepFreeze(o) {  
    var prop, propKey;
    Object.freeze(o); // First freeze the object.
    for (propKey in o) {
        prop = o[propKey];
        if (!o.hasOwnProperty(propKey) || !(typeof prop === 'object') || Object.isFrozen(prop)) {
            continue;
        }

        deepFreeze(prop); // Recursively call deepFreeze.
    }
    return o;
}

function createDeepFreezeObject() {  
    var obj = {
        a: 1,
        b: 2
    };
    return deepFreeze(obj); 
}

for (var i = 0; i < 1000000; i++) {  
    createDeepFreezeObject();
}

The timings are:

real    0m3.715s  
user    0m1.863s  
sys     0m1.634s  

The only modification I have made to the original deepFreeze code is that it returns the modified object. Grouping together the results from above clearly show the impact on execution time:

  • SimpleObject: 30ms
  • Object.freeze: 2900ms
  • deepFreeze: 3700ms

What does the above tell me? Well if I am using Object.freeze or deepFreeze and the objects which I am freezing have a short lifetime and their creation will be frequent, then I will incur a performance impact. If I am not creating objects frequently, inside of a loop and I do value immutability, then honestly I would still use the deepFreeze method. Like anything with Software Development, there has to be a balance and a pragmatic view to things. Different languages bring different considerations to common topics. For example statically typed languages enable you to implement immutability a lot more efficiently and naturally than JavaScript currently does.

Promise Libraries

The following url, https://github.com/cujojs/promise-perf-tests, shows performance tests of different promise libraries and provides information on the different libraries which do and do not use Object.freeze. This repo is no longer maintained at time of writing this.

ECMAScript 6

The draft specification of ECMAcript does contain Object.freeze. http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.freeze