I recently started programming JavaScript and thought everything would be good…
Well today I faced a problem I can’t solve on my own.
My tutorial/ learning project has a class called model. In this class there are several private and one public variable. This variable is of type CustomEvent:
function Model(){
/**
* Array in which the questions are stored
*/
var questions=new Array();
var db;
var valuesSplit="*";
var tableName="quests";
this.myEvent=new CustomEvent("my event");
So as you can see “myEvent” is public and can be called from outside. In this case it is an event which can be subscribed (this is done outside this class by other objects that want to listen) and it can be fired (this is done in the same class). And this is my problem.
How can I access myEvent within the model class?
I tried:
this.myEvent.fire()
and:
myEvent.fire()
But I always get “myEvent is not defined”.
Probably the first thing to say is: JavaScript doesn’t have classes. The sooner you stop thinking of “JavaScript classes,” the better off you’ll be. 🙂 JavaScript has OOP, but not the kind with classes. Your
Modelfunction is called a constructor function.You can access
myEventfrom any code that has a reference to the object created bynew Model, which includes code in your constructor (viathis— e.g., the way you’re setting it up) and any function called withthisreferring to that object (or, of course, “externally” viasomeObjReference.myEvent).So probably
this.myEvent.fire()is what you want, but the code you’re calling it from doesn’t have the rightthisvalue. That’s because in JavaScript,thisis controlled entirely by how a function is called, not where the function is defined as it is in some other languages. See my blog articles Mythical methods and You must rememberthisfor more details, but I’ve done a somewhat truncated discussion below.Here’s an example of a fairly standard way to set up a constructor function with useful methods that all instances share:
Note that that only works if
baris called withthisreferring to an object with amyEventproperty. It’s easy to callbarwiththisset to something else entirely:When the click occurs, the
barfunction gets called, butthisdoes not refer to an object created viaModel. (It will refer to the element with the id “someID” instead.) And so this line inbar…will fail.
If you’re used to class-based languages, you can see how this is totally different from, say, Java, C#, or C++. In those langauges,
thisinsidebarwill always refer to an object created vianew Model. Not so JavaScript, which is both awkward and powerful.This flexibility of functions (not being bound to any particular object) is one of the biggest things to get used to, and take advantage of, in JavaScript. The other is how functions are closures, which is powerful but not complicated.
So if
thisis set by how a function is called, how do you do that? There are two ways:Call the function by referencing it from an object property in the same expression as the call. That’s an awkward way of saying do this:
The expression
f.bar()does two things, which interact: The first thing it does is retrieve the propertybarof the object referenced byf, and get that property’s value (which is a function reference). Then it calls that function (because of the()). The way JavaScript works, because you did those two things in the same overall expression, the interpreter setsthistofduring the call tobarfor you. But note this key distinction:Now when the
barfunction gets called,thiswill not be set tof(it’ll be set to the global object, which iswindowon browsers), because we’ve separated the property retrieval from the function call.Alternately, you can use the built-in features of JavaScript function objects, their
callandapplyfunctions.callandapplydo exactly the same thing, the only difference between them is how you supply the arguments for the function. Example:E.g.,
callandapplyallow you to set whatthisshould be explicitly when you call the function. The only difference between them is thatcallaccepts the arguments to give the function as further arguments tocall, andapplyaccepts them as an array.