Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Friday, August 14, 2015

JavaScript private data using createPrivateThis()

In JavaScript, all object properties are public.

But, coming from Java and a long line of languages before it, I've seen how private properties can enforce better designs.  Is there a convenient way to enforce private properties in JavaScript?

Up to now, there were four proposed solutions:
  1. Imaginary privacy: Put an underscore (_) in front of all private properties to indicate that they are private.
  2. Everything in the constructor: Use closure and attach all your functions to the this object; don't use Object.prototype.func = anymore.
  3. Weakmaps: http://www.nczonline.net/blog/2014/01/21/private-instance-members-with-weakmaps-in-javascript/
  4. Everything public: Why care?
I am now proposing a fifth solution: createPrivateThis().

My objectives were simple:
  1. Provide Java-style public and private data in JavaScript.
  2. Prevent read access to the private data from outside the object.
  3. Allow functions to be created on the prototype object as normal.
  4. Allow functions on the prototype object to access public and private data using the this pointer.
Here's the code that I came up with:
/**
 * Wrap the prototype functions and put the wrappers on the
 * object itself.  A wrapper merges the object and private
 * properties before the call, makes the call then unmerges
 * the object and private properties.
 *
 * Normally, you create your private data in the constructor
 * function and give distinct names to public and private
 * data.  Then, during function calls, you use the this
 * pointer for BOTH public and private data.  In ambiguous
 * situations, the private data is operated on with the
 * exception that adding/deleting data defaults to the
 * (public) object.
 *
 * It is possible to create private functions and variables
 * with the same name using the transitory getPrivateThis()
 * method but this is NOT recommended and is an advanced use.
 *
 * @param {Object} obj The object that needs private data.
 * @return {Object} The private data pointer (this_p).
 */
function createPrivateThis(obj) {
  // use Underscore.js or our own mini version
  var __ = (typeof _ === 'undefined')? {
    isFunction: function(f) {
        return f 
           & {}.toString.call(f) == '[object Function]';
    },
    has: function(o, k) {
      return o.hasOwnProperty(k);
    },
    keys: function(o) {
      return Object.keys(o);
    },
    clone: function(o) {
      var k, o2 = {};
      for (k in o) {
        if (o.hasOwnProperty(k)) {
          o2[k] = o[k];
        }
      }
      return o2;
    },
    each: function(oa, f) {
      var i, k;
      if (oa.constructor === Array) {
        for (i=0; i < oa.length; ++i) {
          f(oa[i]);
        }
      } else {
        for (k in oa) {
          if (oa.hasOwnProperty(k)) {
            f(oa[k], k);
          }
        }
      }
    },
    extend: function() {
      var a, arg, d = arguments[0];
      for (a=1; a < arguments.length; ++a) {
        arg = arguments[a];
        for (var k in arg) {
          if (arg.hasOwnProperty(k)) {
            d[k] = arg[k];
          }
        }
      }
      return d;
    },
    union: function() {
      var a = Array.prototype.concat.apply({}, arguments)
      var b = [], a1, i;
      while (a.length > 0) {
        a1 = a.pop();
        for (i=0; (i < b.length) && (a1 !== b[i]); ++i) ;
        if (i === b.length) {
          b.push(a1);
        }
      }
      return b;
    }
  }: _;
  var priv = {};
  var getPriv = function() {
    return priv;
  };
  // iterate over each function in the object prototype
  __.each(obj.constructor.prototype, function(value, key) {
    if (__.isFunction(value) && !__.has(obj, key)) {
      // intercept the function on the object itself
      obj[key] = function() {
        var pre = __.clone(priv);
        // combine the object and its private properties
        var full = __.extend({}, obj, priv);
        // add the getPrivateThis() access function
        full.getPrivateThis = getPriv;
        // invoke original function with combined object
        var r = value.apply(full, arguments);
        // remove the getPrivateThis() access function
        delete full.getPrivateThis;
        var objKeysToUpdate = {}, objKeysToDelete = {};
        var privKeysToUpdate = {}, privKeysToDelete = {};
        var allKeys = __.union(__.keys(pre), __.keys(priv),
          __.keys(obj), __.keys(full));
        // decide what to do with every key that we ever saw
        __.each(allKeys, function(key) {
          if (__.has(pre, key) !== __.has(priv, key)) {
            // private property was added or deleted
            // using getPrivateThis()
            if (__.has(full, key)) {
              // add/modify object property with the same name
              objKeysToUpdate[key] = full[key];
            } else if (!__.has(full, key) && __.has(obj, key)) {
              // delete object property with the same name
              objKeysToDelete[key] = 'delete';
            }
          } else if (__.has(full, key) && __.has(priv, key)) {
            // modify private property
            privKeysToUpdate[key] = full[key];
          } else if (!__.has(full, key) && __.has(priv, key)) {
            // delete private property
            privKeysToDelete[key] = 'delete';
          } else if (__.has(full, key) && !__.has(priv, key)) {
            // add/modify object property
            objKeysToUpdate[key] = full[key];
          } else if (!__.has(full, key) && __.has(obj, key)) {
            // delete object property
            objKeysToDelete[key] = 'delete';
          }
        });
        // do what we decided to do with the keys
        __.each(objKeysToUpdate, function(value, key) {
          obj[key] = value;
        });
        __.each(objKeysToDelete, function(value, key) {
          delete obj[key];
        });
        __.each(privKeysToUpdate, function(value, key) {
          priv[key] = value;
        });
        __.each(privKeysToDelete, function(value, key) {
          delete priv[key];
        });
        return r;
      };
    }
  });
  return priv;
}

The core idea is to create wrapper functions on the this object which automatically intercept the calls to the functions defined on the prototype object.  The automatically generated interception functions combine the this and the private data, make a call to the prototype function, then uncombine the data back into the this and the private data.

In practice, createPrivateThis() is used like this:
function Animal() {
  var this_p = createPrivateThis(this);
  this_p.nickname = "NoName";
  this.type = 'animal';
}

Animal.prototype.getPrivateNickname = function() {
  // this.nickname is private but we can access it inside here
  return this.nickname;
};

Animal.prototype.setPrivateNickname = function(nick) {
  // this.nickname is private but we can access it inside here
  this.nickname = nick;
};

Animal.prototype.getPublicType = function() {
  return this.type;
};

Animal.prototype.setPublicType = function(type) {
  this.type = type;
};

The private property, nickname, is not accessible outside the object.  It is accessed via the this pointer but it does not actually reside on the object.  The private property actually exists as a local variable in the createPrivateThis() function that is accessed via closure (only available inside the function).

Thursday, August 22, 2013

Backbone.js and Two Dumb Idioms

I've been learning Backbone.js which is MVC (or MV*) for client-side JavaScript.

In Backbone.js, you define a model (the "M") by doing this:

var Model1 = Backbone.Model.extend({
});

You create an instance of a model by doing this:

var model1 = new Model1();

To define a Backbone.js view (the "V"), you can do something like this:

var View1 = Backbone.View.extend({
  events: {
    click: function(e) {
      alert('Clicked!');
    }
  }
});

To create an instance of the view, you can do this:

<a id="a1" href="#"><span>My Link</span></a>
...
var view1 = new View1({el: '#a1'});

Now, here comes Dumb Idiom #1.  You can do this:

alert(view1.$el.css('display'));

Say what?  What's view1.$el?  It's a dumb shorthand that saves two characters.  It means $(view1.el) in regular old jQuery but instead of typing those two parentheses, you can use a shortcut that only works in Backbone.js and easily throws everybody, including yourself, for a loop.

But, wait, there's more.  Here comes Dumb Idiom #2.  Instead of using jQuery find(), you can do this:

alert(view1.$('span').html());

What is this?  In Backbone.js but not standard jQuery, you can use view1.$('span') as a shorthand for $(view1.el).find('span').  We save a few more characters here (i.e. nine) but pay for it with the need to memorize yet another idiom.

Backbone.js, stick to your knitting!  You aren't jQuery and shouldn't be inventing unnecessary and non-standard idioms for jQuery.

Wednesday, July 10, 2013

More Simple JavaScript Inheritance

I like John Resig's Simple JavaScript Inheritance ... except that I hate having to refactor my JavaScript code to use it.

As John suggests, suppose that I have some code:

function Person(isDancing){
  this.dancing = isDancing;
}

Person.prototype.dance = function(){
  return this.dancing;
};

function Ninja(){
  this.dancing = false;
}

Ninja.prototype.dance = function(){
  return this.dancing;
};

Ninja.prototype.swingSword = function(){
  return true;
};

That's normal JavaScript object code.  Now, I decide to use John Resig's "Simple JavaScript Inheritance".  John Resig suggests that I rewrite my code like this:

var Person = Class.extend({
  init: function(isDancing){
    this.dancing = isDancing;
  },
  dance: function(){
    return this.dancing;
  }
});

var Ninja = Person.extend({
  init: function(){
    this._super( false );
  },
  dance: function(){
    // Call the inherited version of dance()
    return this._super();
  },
  swingSword: function(){
    return true;
  }
});

Changes are in red.  The implementations stay the same but the code syntax is different.  It's a bit of a hassle if you have little bit of code and, for a lot of code, it's even more of a hassle.

Why can't I reuse my code as-is with only a few modifications?  Something like this:

function Person(isDancing){
  this.dancing = isDancing;
}

Person.prototype.dance = function(){
  return this.dancing;
};

Person = Class.extend(Person);

function Ninja(){
  this._super( false );
}

Ninja.prototype.dance = function(){
  // Call the inherited version of dance()
  return this._super();
};

Ninja.prototype.swingSword = function(){
  return true;
};

Ninja = Person.extend(Ninja);


Instead of rewriting the code into a new code syntax, I only add an extend() call at the end of each new JavaScript class.  Once I've done that, if I want, I can call this._super() where ever I need to.


I make this possible using a slight modification to John Resig's script.  By checking to see if the argument passed to extend() is a function, instead of an object, the script can handle both syntaxes: John Resig's original syntax and the traditional JavaScript object syntax.

/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * Init-by-function modification
 * By Daniel Howard http://www.svexpertise.com/
 * MIT Licensed.
 */
// Inspired by base2 and Prototype
(function(){
  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

  // The base Class implementation (does nothing)
  this.Class = function(){};

  // Create a new Class that inherits from this class
  Class.extend = function(prop) {
    var _super = this.prototype;
    if ( typeof prop == 'function' ) {
      prop.prototype.init = prop;
      for (var name in prop)
        prop.prototype[name] = prop[name];
      prop = prop.prototype;
    }
   
    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;
   
    // Copy the properties over onto the new prototype
    for (var name in prop) {
      // Check if we're overwriting an existing function
      prototype[name] = typeof prop[name] == "function" &&
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          return function() {
            var tmp = this._super;
           
            // Add a new ._super() method that is the same method
            // but on the super-class
            this._super = _super[name];
           
            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            var ret = fn.apply(this, arguments);        
            this._super = tmp;
           
            return ret;
          };
        })(name, prop[name]) :
        prop[name];
    }
   
    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }
   
    // Populate our constructed prototype object
    Class.prototype = prototype;
   
    // Enforce the constructor to be what we expect
    Class.prototype.constructor = Class;

    // And make this class extendable
    Class.extend = arguments.callee;
   
    return Class;
  };
})();

When a function is detected, instead of an object, the function is first saved to the init property.  Then, all enumerable function properties (which will behave pretty much like static variables in classical class-based systems) are saved to the function's prototype object.  Finally, and here's the magic pixie dust, the function's prototype object is used as the argument which the rest of the code generates the JavaScript class from.  John Resig's code behaves the same but the function's prototype object becomes "the class object" instead of being directly passed as the original argument.

Person = Class.extend(Person);
...
Ninja = Person.extend(Ninja);

The extend() calls above take the JavaScript constructor functions and return the new JavaScript class.  By assigning the return value to the function (variable), the new JavaScript class replaces the traditional JavaScript object creation function.

With this modification and technique, you can write traditional JavaScript object code and, if you later want to use John Resig's "Simple JavaScript Inheritance", you can add it with ease.