Closures vs Objects: FIGHT
In JavaScript, there are two main patterns for creating objects with state - plain JavaScript objects and closures. In this post I’m going to highlight the similarites and consider the pros and cons of the two approaches.
An object is an entity with state and methods to access/modify that state. There are two main approaches to this, which I’ll be calling “objects” and “closures”. For the rest of this post I’ll only use the word object to refer to plain JavaScript objects, in an attempt to avoid confusion.
//object
function Person(name) {
this.name = name;
}
Person.prototype.sayHi = function () {
console.log('Hi there, my name is ' + this.name);
};
//closure
function person(name) {
//using object literal but state held in closure, not in object
return {
sayHi: function () {
console.log('Hi there, my name is ' + name);
}
};
}
Now, there are infinite variations to the above. I’m using native JavaScript
constructors in the object version, but it doesn’t have to be that way. For
example, I could make a function that returns an object, without having to use
new
:
//alternative object
function person(name) {
return {
name: name,
sayHi: function () {
console.log('Hi there, my name is ' + this.name);
}
};
}
Alternatively, I could come up with a more convoluted example that caches the
sayHi
method so it’s shared between instances.
The point is, using objects, state is shared through this
. Whenever you call a
method on an object, e.g.
var dave = new Person('Dave');
dave.sayHi();
this
within the method will be equal to the object it was called upon. When using
closures however, state is shared through the lexical scope. This highlights the
first key difference between objects and closures: access to the internal state.
There are three ways to mutate the internal state of an object in JavaScript. I’ll illustrate with some examples:
//Obtain a reference to the object and assign new properties
function changeName(object, newName) {
object.name = newName;
}
changeName(dave, 'Bob');
//Attach a function to the object and call it as a method on the object
function changeName(newName) {
this.name = newName;
}
dave.changeName = changeName;
dave.changeName();
//call/apply a function with the object as the context
changeName.call(dave, 'Bob');
With closures, on the other hand, there is only one way to mutate the internal state - be inside the scope of the closure:
function person(name) {
//to change `name`, you *must* be defined somewhere inside this function
return {
sayHi: function () {
console.log('Hi there, my name is ' + name);
}
};
}
This can be a blessing and a curse. The advantage of objects over closures is that you’re not limited in the functionality you can add to an object by location in the source code. If you decide that you need to add more functionality to an object, you can do this at any point in your codebase. With a closure the only way to add functionality (with access to the internal state) is to define it somewhere inside the function that creates the closure.
The advantage of closures over objects is the same as the disadvantage but from the perspective of third party code. With an object, anybody can add or change functionality on your object, and access its internal state. With a closure, the internal state is private - it can’t be accessed from outside the closure without the use of accessor functions.
Another advantage that objects have is in terms of memory usage. With a closure,
by definition, for a function to have access to the internal state it must be
defined inside the closure. That means that each new closure created must
have its own version of the function. Objects on the other hand have no such
limitation. A function that reads from and writes to this
need only be defined
once - it can then be added to any object, either shared via the prototype
system or through other means.
An advantage that closures have is that you don’t need to keep track of this
.
With closures it’s simple - you’re either in the correct scope or you’re not.
With objects, if you’re in a method called on an object then this
is that
object, but if the method gets detached from its object (e.g. when passed as an
argument) it loses its binding, or if you have a nested function inside the
method. Then you need to start messing about with call
and apply
, and bind
and other fun stuff.
So, when should you use objects and when closures? If you’re making hundreds of object-type things then they should probably be objects. If there are only a few and you have security concerns then closures are a better bet.
Privacy isn’t just a security issue - it’s useful for creating a clean separation between the public API and the private implementation. Some developers would say that JavaScript doesn’t provide privacy, so you should get used to writing objects with everything public, and tell users of your code to just not touch certain properties (for example those with a leading underscore). I think it’s useful being able to enforce privacy - it makes sure that no-one will ever write code that’s reliant on an implementation detail of yours.
Because of this, I tend to favour closures, falling back to objects when memory becomes a concern, but that’s largely a personal preference.