10

I have a third party flash object which i can manipulate through a javascript API they provided. I am tryind to listen to an event on this object and then fire event inside my object to further bubble up the event. I happen to being using EXT Js but i dont think its important here.

Sample code

this.chart.addEventListener('create', function() {
    this.fireEvent('created');
}, false)

My problem is that 'this' inside the anonymous function refers to the object that fired the event rather than my object that I want to fire an event on.

Its yet another scope issue. Thanks in advance for any help.

bobince
  • 528,062
  • 107
  • 651
  • 834
Jonnio
  • 267
  • 1
  • 3
  • 13

3 Answers3

13

What about creating an external variable before referencing to 'this' object. For example:

var _this = this;
this.chart.addEventListener('create', function() { _this.fireEvent('created'); }, false)
nandokakimoto
  • 1,361
  • 7
  • 14
  • 4
    Yes, this is the normal idiom. The closure variable is often called `that` or `self` (although I don't think the latter's a great idea as `self` has an established — though useless — existing meaning in JavaScript). – bobince Nov 26 '09 at 11:48
  • OK that works. Out of interest is there another way other than setting a variable to this. For example using closure? – Jonnio Nov 26 '09 at 12:12
  • 1
    @bobince: I prefer `self`. `self` is exposed by `window` a reference to itself. I'm quite happy to reuse the identifier in other scopes. It seems to me to carry the right meaning and doesn't clash with that it means when `self` happens to be the window either. – AnthonyWJones Nov 26 '09 at 12:17
5

While the other answers accomplish what you needed, they don't work in the most efficient (scalable) way, because they don't ultimately decouple the view object (this.chart) from that view's logic (fireEvent()). In MVC applications, these view "decisions" reside in a controller. The controller "controls" views and should contain all of the APIs the view may access.

In your example, this is the controller, and that's fine (it means you're adding your listeners in the right place). All you really need to do is bind the handler to the scope of the thing that should do the "handling" -- in your case: this:

// `this` is the controller of `chart`
this.chart.addEventListener('create', function() {
    this.fireEvent('created');
}.bind(this));

What the other answers on this page have done is made it so your view becomes its own controller, but only while handling 'create' events, by assigning a temporary reference to the "controller" using var self = this. Again, this works just fine, but it doesn't work nicely at scale in event-driven applications, and it doesn't really make sense if you have a lot of events to handle.

.bind() is an ECMAScript 5 implementation. If it's needed to work in even older browsers, a good method of doing so is described here (using functions and .call()): https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind

Benny Schmidt
  • 3,230
  • 1
  • 19
  • 14
1

This is the typical approach to this problem:-

(function(self) {
  self.chart.addEventListener('create', function() {self.fireEvent('created');}, false);
})(this);
AnthonyWJones
  • 187,081
  • 35
  • 232
  • 306
  • 2
    I prefer `var self = this;`. Creating a function and then executing it whilst passing `this` as the parameter seems a little overboard - the code is slightly longer too ;-) – Andy E Nov 26 '09 at 12:32
  • @Andy: Yes I often do that too when the code exists in a small execution context. However the above is the standard approach which works in a wider variety of scenarios. The scope of the `self` identifier is limited only to the closure, there is no danger that subsequent code might modify the value contained in it before the event fires. This is not true of the `var self = this;` approach. – AnthonyWJones Nov 26 '09 at 13:13