-
-
Save jfhbrook/1350330 to your computer and use it in GitHub Desktop.
| var util = require("util"); | |
| // Am I the only one that finds this more readable? | |
| // (Trailing super, return constructor) | |
| var inherits = function (inherits, fn) { | |
| util.inherits(fn, inherits); | |
| return fn; | |
| } |
| josh@onix:/tmp/inherits$ node | |
| > var inherits = require("./inherits"); | |
| > var EE = require("events").EventEmitter; | |
| > | |
| > var FooBar = inherits(EE, function () { | |
| ... this.on("foo", function () { | |
| ... console.log("bar"); | |
| ... }); | |
| ... }); | |
| > | |
| > var foobar = new FooBar; | |
| > | |
| > foobar.emit("foo"); | |
| bar | |
| true | |
| > | |
| var util = require("util"); | |
| // This slightly beefier version can also apply your arguments through the | |
| // whole shebang, but this might be better left to do by-hand. | |
| // Alternately, one could use a flag to make auto-application "switch-able". | |
| var Inherits = function (Inheritance, Constructor) { | |
| var Inherited = function () { | |
| Inheritance.apply(this, arguments); | |
| Constructor.apply(this, arguments); | |
| } | |
| util.inherits(Inherited, Inheritance); | |
| return Inherited; | |
| } |
| // Supposing I wanted to make a "really nice" util.inherits... | |
| function inherits (ctor, superCtor) { | |
| ctor.super_ = superCtor; | |
| // Do this.super_.call(this, arg2, arg2) yo'self. | |
| // Alias prototype to proto_ for less typing | |
| ctor.proto_ = ctor.prototype = Object.create(superCtor.prototype, { | |
| constructor: { | |
| value: ctor, | |
| enumerable: false, | |
| writable: true, | |
| configurable: true | |
| } | |
| }); | |
| // Return the constructor | |
| return ctor; | |
| } |
Personally, I use the trick that Javascript hoists function definitions to the top of the block. Hey, if you got it, flaunt it!
var util = require("util");
var events = require("events");
util.inherits(MyEmitter, events.EventEmitter);
function MyEmitter () {
events.EventEmitter.call(this);
// My code
}It's a slight pain to call the "superclass" constructor, but Python requires that too so it's a wash.
Yeah, plus I can imagine times where you would want to inherit prototypes but not call the constructor with the same arguments, so it's good that this is separable.
I like what you did there. Will that work with var MyEmitter = function () { ... } as well? I tend not to get too crazy with exploiting hoists.
-1. For a few reasons:
- What we have already (minimally sweetened with the HFCS-free util.inherits, or my own https://github.com/isaacs/inherits/blob/master/inherits.js) is powerful enough to do even fairly sophisticated OOP voodoo.
- Returning the ctor is annoying. You lose function hoisting, which is nice.
- At least in my experience it's very rare that you need to pass the same exact arguments to the superclass constructor, and even more rare that you have an indeterminate number of arguments to a constructor.
To your credit, however, at 7 lines, inheritsWithSameArguments is within 10-line sanity limit for javascript OOP utilities.
I think it does not work with var MyEmitter = function () { ... }. Even if it did work, I would personally avoid it on aesthetic or confusion grounds.
I agree about hoisting sparingly. I only use if the function definition immediately follows the reference. But I am now experimenting with using this to make EventEmitter code more clear.
var emitter = new EventEmitter({"or":"some", "subclass":"like", "pipe":"or whatever"});
emitter.on('end', process_results);
emitter.on('error', function(er) {
console.error("Sorry: " + er);
})
function process_results() {
console.log("Yay! Everything finished");
do_stuff(function(er) {
if(er) {
console.error("But I failed to do stuff: " + er);
} else {
console.log("And we're done!");
}
})
}The jury is still out. I'll try it for a few months. Thoughts.
- By labeling the "end" handler, you are documenting the code a little more. (
on_enddoesn't count, think of something meaningful.) - The code reads more chronologically. It makes sense that "end" code is at the bottom.
- In Python and other "good" languages, you can't do this at all. You must define the function before referencing it. You must tell the person reading your code "Okay, STOP! Please remember the state of everything. We interrupt this program to bring you some random function definition." Then x lines of code later, you tell the reader, "We now return to your regular program."
- This sort of code usually itself lives in a function (a callback, or a constructor), so you are defining nested Javascript functions (one of the "good parts" if I recall). The namespace stays nice and tidy.
Real-world example: https://github.com/iriscouch/probe_couchdb/blob/master/couch.js#L103-120. I didn't go crazy, just small, simple callbacks.
What we have already ... is powerful enough to do even fairly sophisticated OOP voodoo.
Yeah, definitely. I wouldn't expect any sugar to do much more than a ctor-returning util.inherits. I'm personally not big on inheritance; Usually I just push objects around between functions. I also worry that adding too much sugar can obfuscate what you're really trying to do. That said, I feel like it could use some help in the readability department. To each his own, I guess.
Returning the ctor is annoying. You lose function hoisting, which is nice.
Is it? Like I said, I tend not to exploit hoisting. On the other hand, the fact that this changes this behavior does mean that returning the ctor isn't backwards-compatible; That is, this change would break code like Jason's. (Edit: Unless I misunderstand what you're saying. This seems like a weird behavior to me, even for javascript.)
it's very rare that you need to pass the same exact arguments to the superclass constructor
I think this is a matter of style. What if your constructors all take just an options hash? I was just reading code that does this, three levels of util.inherits deep. I agree, though, that it's less general. In retrospect, I would also -1 adding passing the arguments but could go either way on returning the ctor.
Plus, it's not like I can't just write my own utils, with blackjack and hookers.
and even more rare that you have an indeterminate number of arguments to a constructor
Well, yeah, but I don't know what that number of arguments is going to be a'priori so I'd have to do it that way for n arguments.
The jury is still out. I'll try it for a few months.
I might have to start doing that myself.
absolutely! I think the trailing inline function is much more readable (and returning the constructor), it's how i've designed the jay library type functions. I've also got two options, one where you specify the constructor directly, and one where you define the prototype object which can optionally contain a new subconstructor. you can check it out at http://github.com/ngspinners/jay
Oh, and as far as making util.inherits return the constructor, I submitted a pull request here: nodejs/node-v0.x-archive#2056
(Note, the pull request was for the proposal in the comments, not to replace util.inherits with my gist or anything like that.)