Originally published at: http://www.sitepoint.com/using-closure-space-create-real-private-members-javascript/
This article is part of a web dev tech series from Microsoft. Thank you for supporting the partners who make SitePoint possible.
I recently developed Angular Cloud Data Connector, which enables Angular developers to use cloud data, specifically Azure mobile service, using web standards like indexed DB. I was trying to create a way for JavaScript developers to embed private members into an object. My technique for this specific case is to use what I call “closure space”. In this tutorial, I want to share with you how to use this for your own projects and how performance and memory are impacted for the major browsers.
But before diving into it, let me share why you may need private members, as well as an alternate way to “simulate” private members.
Feel free to ping me on twitter if you want to discuss this article.
Why use private members
When you create an object using JavaScript, you can define value members. If you want to control read/write access on them, you need accessors that can be defined like this:
var entity = {}; entity._property = "hello world"; Object.defineProperty(entity, "property", { get: function () { return this._property; }, set: function (value) { this._property = value; }, enumerable: true, configurable: true });
Doing this, you have full control over read and write operations. The problem is that the _property member is still accessible and can be modified directly.
This is exactly why you need a more robust way to define private members that can only be accessed by object’s functions.
Using closure space
The solution is to use closure space. This memory space is built for you by the browser each time an inner function has access to variables from the scope of an outer function. This can be tricky sometimes, but for our topic this is a perfect solution.
So let’s alter the previous code to use this feature:
var createProperty = function (obj, prop, currentValue) { Object.defineProperty(obj, prop, { get: function () { return currentValue; }, set: function (value) { currentValue = value; }, enumerable: true, configurable: true }); } var entity = {}; var myVar = "hello world"; createProperty(entity, "property", myVar);
In this example, the createProperty function has a currentValue variable that get and set functions can see. This variable is going to be saved in the closure space of get and set functions. Only these two functions can now see and update the currentValue variable! Mission accomplished!
The only caveat we have here is that the source value (myVar) is still accessible. So here comes another version for even more robust protection:
var createProperty = function (obj, prop) { var currentValue = obj[prop]; Object.defineProperty(obj, prop, { get: function () { return currentValue; }, set: function (value) { currentValue = value; }, enumerable: true, configurable: true }); } var entity = { property: "hello world" }; createProperty(entity, "property");
Using this method, even the source value is destructed. So mission fully accomplished!
Performance Considerations
Let’s now have a look at performance.
Obviously, closure spaces or even properties are slower and more expensive than just a plain variable. That’s why this article focuses more on the difference between regular way and closure space technique.