battlepope
battlepope

Reputation: 300

Javascript: How to access a public variable in the same class?

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

Answers (3)

T.J. Crowder
T.J. Crowder

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:

  1. 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.

  2. 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

styrr
styrr

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

gion_13
gion_13

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

Related Questions