0

I am trying to build a small javascript library for study purposes. I have an object inside a prototype that has some functions inside. I would like to access this (the one that has valueOf and stuff) from inside the functions, but this now brings me only the object parent to the functions.

My code (coffee):

HTMLElement.prototype.on =
  click: (action) -> @valueOf().addEventListener('click', action, false)
                     # I'd like this to work,
                     # but `this` here refers to the `on` object

Is this possible? I know I'm kinda reinventing the wheel, but as I said, it's for study purposes, so that's the idea.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
André Leria
  • 392
  • 3
  • 17
  • In short: No, it's impossible. – Bergi Aug 30 '13 at 15:25
  • Which `this` are you referring to? You said "(the one that has valueOf and stuff)" but I am not understanding. – Alberto Zaccagni Aug 30 '13 at 15:26
  • 1
    possible duplicate of [Organize prototype javascript while perserving object reference and inheritance](http://stackoverflow.com/questions/15884096/organize-prototype-javascript-while-perserving-object-reference-and-inheritance) or [prototype: deep scope of “this” to access instance's scope](http://stackoverflow.com/questions/16502467/prototype-deep-scope-of-this-to-access-instances-scope) – Bergi Aug 30 '13 at 15:27
  • I believe it's a duplicate, but I hadn't seen this other question before. I'll check if it answers my question... – André Leria Aug 30 '13 at 15:28
  • @Alberto: the `this` which contains the `HTMLElement` instance's data. – André Leria Aug 30 '13 at 15:31

2 Answers2

1

I am not 100% sure about the "this" the OP is referring to but I'll try to answer:

Let's first compile your coffee-script code to javascript:

HTMLElement.prototype.on =
  click: (action) =>> @valueOf().addEventListener('click', action, false)

HTMLElement.prototype.on = {
  click: function(action) {
    return this.valueOf().addEventListener('click', action, false);
  }
};

Ok, all right. In javascript the value of this in a function can be determined by many factors, one of which is a MemberExpression. When you call a function with a.b(), where a is an object, this inside the function b will be bound to a. In your case you are probably calling the function like that:

element.on.click(...);

The issue is that element.on is itself an object. The expression will evaluate to

(element.on).click(...);

and the value of this in the click function will be the object prototype.on, not element.

Basically, don't do it that way. It will be a pain to bind this to the HTMLElement instance. Put the function directly on the prototype. The only solution I can think of (terrible, shame on me) is:

element.on.click.bind(element)(...)
Aegis
  • 1,749
  • 11
  • 12
  • That indeed is my problem. Now I just need a solution that is better than repeating `element`, but I'm starting to think that's impossible with javascript (that is, if something is impossible in JS). – André Leria Sep 04 '13 at 13:51
  • @AndréLeria well, just put functions on the prototype, not objects. You could have something like prototype.onClick, or prototype.bind('click', function () { ... }). – Aegis Sep 04 '13 at 14:31
  • I know, but that already exists. I just wanted to organize stuff better. But I think I've worked around this, and I may soon be posting my resolution to it here... – André Leria Sep 04 '13 at 15:54
  • 1
    @AndréLeria I don't think it is "better organization" but you can do something like `function EventHandler(element) { ... }`; `function Element() { this.on = new EventHandler(this); }` – Aegis Sep 04 '13 at 16:24
  • That looks like the way to go. I was thinking on something like that, but built directly on top of a coffee class. The solution is basically keeping an instance of `this` inside another object, as `this` itself will always be overwritten. – André Leria Sep 11 '13 at 15:36
0

If I understand your problem correctly, the solution is the => Operator.

http://coffeescript.org/#fat-arrow

treeno
  • 2,510
  • 1
  • 20
  • 36