Archives Posts
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