Perfection Kills

by kangax

Exploring Javascript by example

← back 337 words

Semantic constructors

One of the easiest ways to inspect an object is to type convert it to a string:


function foo() {
  return 'foo';
}
foo + ''; // "function foo(){ return 'foo'; }"
// or
foo.toString();
// or
String(foo);

When using Class.create from prototype.js, I often get annoyed by a meaningless result of constructor’s toString:


var Person = Class.create({
  initialize: function(name) {
    this.name = name;
  },
  speak: function(msg) {
    return this.name + ' says: ' + msg;
  }
});

Person + ''; // "function klass() { this.initialize.apply(this, arguments); }"

var Employee = Class.create(Person, {
  initialize: function($super, dept) {
    $super();
    this.dept = dept;
  }
});

Employee + ''; // "function klass() { this.initialize.apply(this, arguments); }"

As you can see, constructor’s default toString tells little about what’s going on under the hood. In fact, Class.create returns a generic constructor-function which only calls initialize method of a prototype. In other words it acts as a proxy. One of the ways to see actual constructor’s code is to inspect “initialize” method directly:


// nothing new here
Person + ''; // "function klass() { this.initialize.apply(this, arguments); }"

// and the actual code
Person.prototype.initialize + ''; // "function (name) { this.name = name; }"

This is quite verbose, don’t you think? Let’s monkey patch Class.create a little:


Class.create = (function(original) {
  var fn = function() {
    var result = original.apply(null, arguments);
    result.toString = function() { return result.prototype.initialize.toString() };
    return result;
  };
  fn.toString = function(){ return original.toString() };
  return fn;
})(Class.create);

We simply redefine original Class.create with an “enhanced” version. New version “binds” toString of prototype.initialize as toString of constructor-function. As a bonus, it also takes care of a “wrapped” Class.create.toString itself – trying to be as unobtrusive as possible:


// monkey-patch Class.create first...

// Person.toString now yields much more informative result
Person + ''; // "function (name) { this.name = name; }"

// Class.create code seems to be unchanged
Class.create + ''; // outputs actual Class.create code

Did you like this? Donations are welcome

comments powered by Disqus