4

I'm a JavaScript slightly-more-than-beginner.

While reading the source for EventEmitter, I stumbled upon this interesting and, to me, elegant function:

// alias a method while keeping the correct context
function alias(name) {
    return function aliasClosure() {
        return this[name].apply(this, arguments);
    };
}

I have two main questions:

First: why is the aliasClosure a named function? Is it useful in some way other than clarity? Also, is is really a closure? To me, it looks just like a semi-anonymous function.

Second: I rewrote this function like this:

function alias2(name) {
    return this[name].bind(this);
}

Is it equivalent? I think it should, since the this context is the same and it's preserved in both versions.

Is there a reason to prefer one over the other?

Marcin
  • 48,559
  • 18
  • 128
  • 201
whatyouhide
  • 15,897
  • 9
  • 57
  • 71
  • See second answer here for a great javascript example of a closure. http://stackoverflow.com/questions/36636/what-is-a-closure – hookenz Aug 27 '13 at 21:51

2 Answers2

2

No, these are not at all equivalent. From looking at the alias() function I think you would use it something like this:

> Array.prototype.strjoin = alias('join');   // make 'strjoin' an alias of 'join'
> [1, 2, 3].strjoin(" + ");
"1 + 2 + 3"

Using alias2() in the above code will not work.

Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
1

Providing a name in a function instantiation expression makes a name available for stack traces. (I'm told newer debuggers don't always need it if the function is created in certain contexts, like a var initialization.)

I think the second is equivalent, mostly, though .bind() has some obscure special cases it handles.

edit wait - no, they're not equivalent. The first one involves this explicitly, and performs the lookup on each call. The first function doesn't need this to be bound to anything when it's called, while yours will throw an exception in that case.

One change that would make the two functions almost equal is wrapping bind inside a closure, like this:

function alias2(name) {
    return function() {
        return this[name].bind(this);
    }
}

Still, bind behaves obscurely in rare cases.

whatyouhide
  • 15,897
  • 9
  • 57
  • 71
Pointy
  • 405,095
  • 59
  • 585
  • 614
  • Hi downvoter - I just updated the answer because I changed my mind :) – Pointy Aug 27 '13 at 21:49
  • Ok, fair enough. So the second version could be buggy in some contexts? I guess so, since it binds `this` to the moment I call `alias2`, and then it stays the same. – whatyouhide Aug 27 '13 at 21:54
  • @whatyouhide right - I just added a little more. It's kind-of confusing :) – Pointy Aug 27 '13 at 21:56
  • Additionally, named functions can also be used for recursion, no matter what variable the function is assigned to. Not used here, just a note :) – Félix Saparelli Aug 27 '13 at 21:57
  • @FélixSaparelli thank you, yeah I knew of using that for recursion, just here it didn't seem useful :) – whatyouhide Aug 27 '13 at 21:58
  • @FélixSaparelli oh yes you're right - that's actually kind-of important; Erlang anonymous functions don't have that (or they didn't, last time I checked), and that kind-of hurts. – Pointy Aug 27 '13 at 21:59
  • @Pointy Ok, thank you. Still, by wrapping my `bind` inside a closure, would it work? Just by replacing `return this[name].apply(this, arguments);` with `return this[name].bind(this)`? If so, would it be different in some other way? – whatyouhide Aug 27 '13 at 21:59
  • @whatyouhide yes I think that'd be closer, though again `.bind()` does a couple of obscure things (that rarely matter). – Pointy Aug 27 '13 at 22:02
  • @Pointy Ok, that's all. Thank you very much :) – whatyouhide Aug 27 '13 at 22:03
  • @FélixSaparelli ah yes; the replacement would have to bind it *and* invoke the return value. – Pointy Aug 27 '13 at 22:09
  • Sorry, deleted my comment when I saw your edit, but before I saw your reply. For later reference, I was pointing out the differences between `bind` and `apply`, and noting that OP's `alias2()` would throw a `TypeError` if `this[name]` was undefined, while `alias()` would not (`alias('foobar')()` *would*). – Félix Saparelli Aug 27 '13 at 22:12