Why use Object.freeze

March 15 2015: An update to this post has been written: Why use Object.freeze and why not to use Object.freeze.

I like JavaScript but I also like Encapsulation and Immutability.

There is lots of information on the internet, in books, whitepapers etc... about the benefits of Encapsulation and Immutability so I will not go through these in this post. I simply want to show the facts of what you get when you use the Object.freeze method, and also conversely what are the effects when you do not use it.

When you are working in a development team with a size greater than 1, you want to ensure that the development experience is efficient and consistent from the team and not just from your own work. You also expect the same back from the members of your team. Working with a dynamic language can introduce problems such as type/value betrayal. If you set a value of 1 and somewhere along the codebase this value changes to "1" then you are undoubtably going to get errors and side effects which you ultimately have not accounted for.

The first example shows a simple object which exposes its inner property. The test shows that we are free to modify the property directly.

describe('without encapsulation', function() {  
    var OpenPerson = function(age) {
        return {
            age: age
        };
    };

    it('state can be changed', function(done) {
        var p = new OpenPerson(0);
        p.age++;
        p.age.should.eql(1);
        done();
    });
});

The next example shows how we can achieve encapsulation without using Object.freeze. Things to notice here is that it requires you to expose the property via a function. This does prevent the value from being changed but it does not guard from the function being deleted.

describe('achieving encapsulation without Object freeze', function() {  
    var FunctionClosedPerson = function(age) {
        return {
            age: function() {
                return age;
            }
        };
    };

    it('results in the use of function based declaration', function(done) {
        var p = new FunctionClosedPerson(1);
        var age = p.age();
        age++;
        age.should.eql(2);
        p.age().should.eql(1);
        done();
    });

    it('still allows the deletion of the member', function(done) {
        var p = new FunctionClosedPerson(1);
        delete p.age;
        should(p.age).eql(undefined);
        done();
    });
});

The final example is using Object.freeze. Here we see that we maintain encapsulation, we maintain property based access and also we know that our objects will stay the way we left them. The errors which I assert on here show that the property is now recognized formally as readonly and is unable to be deleted.

describe('achieving encapsulation with Object freeze', function() {  
    var FrozenPerson = function(age) {
        return Object.freeze({
            age: age
        });
    };

    it('allows for property based access as opposed to function based access', function(done) {
        var p = new FrozenPerson(1);
        p.age.should.eql(1);
        done();
    });

    it('does not allow modification of any property', function(done) {
        var p = new FrozenPerson(1);
        should(function() {
            p.age++;
        }).
        throw (/Cannot assign to read only property 'age'/);
        done();
    });

    it('does not allow removal of members of a frozen object', function(done) {
        var p = new FrozenPerson(1);
        should(function() {
            delete p.age;
        }).
        throw (/Cannot delete property 'age'/);
        done();
    });
});

When working on a node.js project, the above gives you great benefits in the fact that this is one less thing which you need to test for i.e. "Is my property still there?" and "Is the type of my property what I expect?". I say node.js and not JavaScript as you could be developing JavaScript in the Browser which means that Object.freeze support will differ. Doing the JavaScript development on the node.js platform alleviates this issue, as is the target and the context for this POST. Saying that I would still aim to do this in as many places as I can even in Browser based development, providing sufficient handling for those browsers which do not support this.

An alternative to this would be to lean on your test suites and extend to assert on property existence and type (as much as possible in JavaScript). I would expect both the above and rigorous testing, only not testing for the things which I now have nailed using the Object.freeze method.

I would still recommend that careful thought is given before any decision is made to use getters and setters. It is much more beneficial to identify and model behaviours and hide as much information as is possible inline with "the principle of least knowledge."