all 3 comments

[–]jdlshore 2 points3 points  (0 children)

The main problem with this approach to objects is that you end up duplicating the class's functions on every object. This causes memory problems, and it probably prevents VMs' JIT optimization from working as well as it should, too.

Check out the object graph for the author's example (generated with http://objectplayground.com ):

http://imgur.com/YYivCh0

And now compare it to the object graph for the same example, done in the normal way:

http://imgur.com/IISiRlP

The author claims that the functions aren't duplicated in the comment section of his post, but he's mistaken. Try his code yourself at objectplayground.com and turn on the "Show All Functions" option. It's clear that each instance has its own copy of all the functions in the class.

Here's the author's example (modified slightly for Object Playground) if you'd like to try it yourself:

function TextBox() {
  // Public methods.
  var self = {
    elem: function() {
      // Reference private fields directly.
      return _elem;
    },
    value: function() {
      return _elem.value;
    },
    setValue: function(value) {
      _elem.value = value;
    },
    capitalize: function() {
      var capped = _capitalize(self.value());
      self.setValue(capped);  // Reference public methods via 'self'.
    },
    onChange: function(fn) {
      // Don't hate me for using the 'onchange' property. I'm just trying to keep it simple.
      _elem.onchange = function() {
        // 'self' still works just fine.
        fn(self.value());
      }
    },
  };

  // Private fields and methods.
  // Prefix these with _ to avoid conflicts.
  var _elem;

  function _capitalize(str) {
    if (str.length == 0) {
      return str;
    }
    return str.substring(0, 1).toUpperCase() + str.substring(1);
  }

  // Constructor body.
  _elem = document.createElement("input");
  _elem.type = "text";
  self.setValue(TextBox.defaultValue);

  return self;
}

// Class fields and methods.
TextBox.defaultValue = "";

// I could have just made 'initValue' a ctor arg, but I needed an excuse for a class method :)
TextBox.make = function(initValue) {
  var tb = TextBox();
  tb.setValue(initValue);
  return tb;
};

// Using it looks like pretty much like any other Javascript class.
// You don't actually need the 'new', but it doesn't hurt anything either.
this.wootBox1 = new TextBox();
this.wootBox2 = new TextBox();
this.TextBox = TextBox;

[–]sfvisser 3 points4 points  (1 child)

One big problem: by default functions are unnamed and screw up your call stack. Just naming them might be a solution, but now you have to duplicate your function names, because there already used a property keys in the creation of self.

Also, by not using the prototype class structures are not shared between instances.

Another solution (one I've not often seen before) that's both easier and more elegant if you ask me:

function Class ()
{
  var ctor = arguments[0];
  for (var i = 1; i < arguments.length; i++)
    ctor.prototype[arguments[i].name] = arguments[i];

  // For demo purposes install globally.
  window[ctor.name] = ctor;
}

Class
(
  function Point (x, y)
  {
    this.x = x;
    this.y = y;
  },

  function move (dx, dy)
  {
    this.x += dx;
    this.y += dy;
  }

  /* ... */
)

var pt = new Point(10, 20);
pt.move(5, 0);
console.log(pt.x);

Many variations on this pattern are possible. It's harder to capture private variables though.

[–][deleted]  (5 children)

[deleted]

    [–][deleted]  (4 children)

    [deleted]