util.inherits vs. CoffeeScript extends

925 views
Skip to first unread message

Matthew Leon Grinshpun

unread,
Jun 27, 2011, 12:10:00 AM6/27/11
to nodejs
Node's util.inherits mechanism (https://github.com/joyent/node/blob/
master/lib/util.js) for object inheritance works somewhat differently
than the 'extends' keyword in CoffeeScript, which generates the
following code:

var __hasProp = Object.prototype.hasOwnProperty, __extends =
function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key))
child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
};

In practical use, I have yet to notice any disadvantages to using the
CoffeeScript extends. I may just have been lucky so far, though. Does
it present any dangers when applied to Node constructors that I may
not have noticed? Does anyone know of some examples where it would be
inappropriate?

Thanks,

MLG

Aseem Kishore

unread,
Jun 28, 2011, 1:29:19 PM6/28/11
to nod...@googlegroups.com, coffee...@googlegroups.com
That's a good question; +coffee list.

They're pretty similar; both create an intermediate instance of an object to connect the child and the parent; i.e. the child's prototype isn't an instance of the parent directly. And they both add two properties: a "super" static property, and a "constructor" instance property.

The only difference I can tell in the end result is that Coffee also copies all static properties/methods from the parent to the child, whereas Node's util.inherits does not. So e.g. if Child extends Parent, and you've defined Parent.staticA, the same property/method will also be available at Child.staticA.

The only difference I can tell in the implementation is that Node uses ES5 capabilities while Coffee does not, in two places: (a) in the constructor of the intermediate instance and (b) in defining the "constructor" instance property. For (a), Node uses Object.create() while Coffee uses the new operator. Not sure if this is significant. For (b) Node uses a property descriptor that ensures the constructor property won't be enumerated. Coffee's constructor property *will* be enumerated, but since it's not on the child instance directly, it won't be enumerated if you filter via hasOwnProperty().

Good question again! And I hope this is both accurate and helpful.

Cheers,
Aseem

--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

Aseem Kishore

unread,
Jun 28, 2011, 1:30:31 PM6/28/11
to nod...@googlegroups.com, coffee...@googlegroups.com
Btw, Coffee doesn't use ES5 by design, since it's designed to work as far back as IE6, so given that constraint, Coffee's extends seems about as good as you can get, which is pretty awesome. Node's util.inherits is also rad; it just doesn't copy static properties.

Aseem

Marco Rogers

unread,
Jun 28, 2011, 3:09:39 PM6/28/11
to nod...@googlegroups.com, coffee...@googlegroups.com
I would go further and say node uses the most up to data capabilities of v8, because it doesn't need to worry about browser compatibility (that's not entirely true, but at least not in this instance). Where as coffeescript wants to work everywhere javascript works. You should NOT use util.inherits in a hybrid module that will run in the browser unless you KNOW it will only be deployed in newer environments with these es5 capabilities. For instance, Object.create cannot be properly shimmed into older browsers and have the same semantics.

If it's a node-only module, then use whatever makes you feel good as long as you understand the subtle differences. Aseems explanation is very good. There is another important difference though. util.inherits provides child.super_ as the parent *constructor* itself. This allows you to chain constructor calls, usually like so.

function Child() {
  Child.super_.apply(this, arguments);
}

util.inherits(Child, Parent);

The only real benefit to this is that it keeps you from using the actual ancestor name in the Child constructor. You could change the name of Parent or even refactor and use some other constructor and Child would be none the wiser. Not that you have to *explicitly* call parent constructor. It won't be done for you. But this is also equally valid.

function Child() {
  Parent.apply(this, arguments);
}

util.inherits(Child, Parent);

It seems that CS extend provides Child.__super__ as the parent *prototype*.  I haven't used CS at all so I excuse me if I get this wrong. But at a glance, it looks like the CS class abstraction sets things up so you don't need to call the parent constructor explicitly. It uses this __super__ property to look it up and call it for you. See http://jashkenas.github.com/coffee-script/#classes

Hope this helps. This kind of core understanding of prototypal inheritance and the various abstractions around it are pretty important I think. And it's hard to get a handle on the differences between various solutions.

:Marco

Reply all
Reply to author
Forward
0 new messages