Adding Support for Private Data Members
Problem
You’ve discovered reusable functionality and created an object with the functionality
defined as object methods. However, you also need to add support for private data
members, too.
Solution
One approach to ensuring a data member isn’t exposed to public access is to redefine
the object as a function with publicly exposed methods or data objects that use the
private data or methods. If we redefined the functionality of the objecT,
modifying the string methods to use a replacement string, we could store the replace‐
ment string as private data:
var StrManipulate = function() { var replacementText = "**"; this.replaceSlashes = function(str) { return str.replace(/\\/g, replacementText); }; this. replaceAngleBrackets = function(str) { return str.replace(//g,replacementText); }; }; And create a new instance of the object:
var strObj = new StrManipulate(); console.log(strObj.replaceAngleBrackets(""); // "**html**"However, if we don’t want to use a declared function, or abandon our use of an object, we can encapsulate that object in an IIFE:
(function() { var replacementStr = "**"; this.jscb = { // return element getElem : function (identifier) { return document.getElementById(identifier); }, replaceSlashes : function(str) { return str.replace(/\\/g, replacementStr); }, replaceAngleBrackets: function(str) { return str.replace(/ /g,replacementStr); } }; })();
EXPLAIN
A simple function-to-object conversion is easier on the global namespace than separate
functions, but a one-off object of this nature has disadvantages, including an inability
to add private data accessible by all of the object methods.
One approach is to convert the object to a declared function and expose all of the meth‐
ods, but not the data, via this, as shown in the example. For years, this was the approach
to use when creating libraries.
However, a more future-proof solution, which is also more elegant and efficient, is to
use an IIFE to encapsulate the object’s methods and data.
This approach creates a sin‐
gleton—a single object—rather than an object that’s instantiated. It also allows you to
use either a function as the core of the library object, or an object, as shown in the
solution.
You can even ensure that the object is a singleton. I adapted the object created in the
solution into a true singleton, following the Singleton implementation in Addy Osmani’s
Learning JavaScript Design Patterns.
shows the new implementation, and
a test case. The main difference is that a singleton implementation is created with
init(), which is called when the getInstance() function is called. If the instance al‐
ready exists, though, the original instance is returned—a new instance is not created,
as the strict equality (===) tests demonstrates.
Converting library object into Singleton
var jscbSingleton = (function() { var instance; function init() { var replacementStr = "**"; return { // return element getElem : function (identifier) { return document.getElementById(identifier); }, replaceSlashes : function(str) { return str.replace(/\\/g, replacementStr); }, replaceAngleBrackets: function(str) { return str.replace(/ /g,replacementStr); } }; } return { // Get the Singleton instance if one exists // or create one if it doesn't getInstance: function () { if ( !instance ) { instance = init(); } return instance; } }; })(); var jscb = jscbSingleton.getInstance(); var jscb2 = jscbSingleton.getInstance(); console.log(jscb === jscb2); // true var str = jscb.replaceAngleBrackets(""); console.log(str);
There are no absolutes when it comes to creating a reusable library or utility object. I demonstrated a couple of approaches in this and the last few sections, but they’re just a start.
The best way to determine how to implement your library object is to examine existing ones. I suggest taking a look at the source code for jQuery and Underscore, both of which were covered earlier in the chapter, and Lightbox 2, which I mentioned all of these can be discovered in GitHub.
No comments:
Post a Comment