Reputation: 300
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".
Upvotes: 3
Views: 2137
Reputation: 1074425
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 Model
function is called a constructor function.
You can access myEvent
from any code that has a reference to the object created by new Model
, which includes code in your constructor (via this
— e.g., the way you're setting it up) and any function called with this
referring to that object (or, of course, "externally" via someObjReference.myEvent
).
So probably this.myEvent.fire()
is what you want, but the code you're calling it from doesn't have the right this
value. That's because in JavaScript, this
is 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 remember this
for 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:
function Foo() {
this.myEvent = new CustomEvent("my event");
}
Foo.prototype.bar = function() {
this.myEvent.fire();
};
// Usage:
var f = new Foo();
f.bar(); // Fires the event indirectly, via the code in `bar`
f.myEvent.fire(); // Fires it directly
Note that that only works if bar
is called with this
referring to an object with a myEvent
property. It's easy to call bar
with this
set to something else entirely:
document.getElementById("someID").onclick = f.bar;
When the click occurs, the bar
function gets called, but this
does not refer to an object created via Model
. (It will refer to the element with the id "someID" instead.) And so this line in bar
this.myEvent.fire();
...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, this
inside bar
will always refer to an object created via new 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 this
is 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:
var f = new Foo();
f.bar(); // <== The key bit
f["bar"](); // <== Also works
The expression f.bar()
does two things, which interact: The first thing it does is retrieve the property bar
of the object referenced by f
, 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 sets this
to f
during the call to bar
for you. But note this key distinction:
var f = new Foo();
var b = f.bar;
b(); // <== Different!
Now when the bar
function gets called, this
will not be set to f
(it'll be set to the global object, which is window
on 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 call
and apply
functions. call
and apply
do exactly the same thing, the only difference between them is how you supply the arguments for the function. Example:
var f = new Foo();
f.bar(1, 2): // <== Calls `bar` with `this` === `f` and passing in
// the arguments 1 and 2
var b = f.bar;
b.call(f, 1, 2); // <== Does the same thing as f.bar(1, 2)
var args = [1, 2];
b.apply(f, args); // <== Does the same thing as f.bar(1, 2)
E.g., call
and apply
allow you to set what this
should be explicitly when you call the function. The only difference between them is that call
accepts the arguments to give the function as further arguments to call
, and apply
accepts them as an array.
Upvotes: 7
Reputation: 829
If you want to use myEvent
in a non public function in your Model
than you have to create another reference to the myEvent
which isn't using the this
reference of your Model. Because the this
in another function is something else than in your Model
function. The easiest way to bypass this problem is if you define a new variable in your Model
:
var that = this;
Then you can call your myEvent
like:
that.myEvent
Upvotes: 1
Reputation: 41533
The myEvent
member should be visible both in the inner & outer scope of the object :
function Model(){
this.myEvent = 'some value';
this.canAccessEvent = function(){
return 'myEvent' in this;
}
}
var m = new Model();
// access from outside :
alert(m.myEvent);
// access from inside :
alert('Model can access the even? ' + m.canAccessEvent());
However, it is very possible that your new CustomEvent
function doesn't exist or does not return a valid object making your myEvent
variable to be undefined
. I suggest you attribute some other value to the myEvent
property and see if it is defined. If it is defined, then the problem lies in your CustomEvent
function.
Upvotes: 0