views:

299

answers:

4
+2  Q: 

JS Variable Scope

Variable scope in JS is confusing the hell out of me. In follwing code, if i use the setClient public method to set a clientID i can then access the value from inside the track method using the getClient method. I cannot however access the value of the private member 'version' this way (or any other private member).. i had assumed that var _this = this would create a closure of some sort allowing access to the scope of the Container function.

And now i'm confused. I realise this is probably really simple though, so I thought i'd ask here. Where on earth have a grasped the wrong end of the stick?

function Container()
{
    // private members
    var version = '0.1';
    var CID = false;
    var _this = this;

    // public members
    this.getVersion = function() { return _this.version; }
    this.getClient = function() { return _this.CID; }
    this.setClient = function(CID) { _this.CID = CID; }

    // private methods
    this.getQS = function() { return _this.version; }

    // public methods
    this.track = function()
    {
        if (_this.CID)
        {
            var date = new Date();

            data = 
            {
                cid: _this.getClient(),
                sw: screen.width ? screen.width : false,
                sh: screen.height ? screen.height : false,
                d: date.getTime()
            }

            qs = '';

            for (p in data) { qs += p+'~'+data[p]+'-'; }

            var elHd = document.getElementsByTagName("head")[0];         
            var elScr = document.createElement('script');

            elScr.type = 'text/javascript';
            elScr.src = 'http://example.org/'+qs+
                            'version-'+_this.getVersion();

            elHd.appendChild(elScr);
        }
        else
        {
            alert('no client ID');
        }
    }
}
A: 

I'm not sure I'm understanding where you are confused (or why you are doing things the way you are, so I may be meta-confused). What happens if you just do:

this.getVersion = function() { return version; }
MarkusQ
A: 

The variable version is not a member field of the Container class. It is a local var that only exists for the duration of the Container constructor. You need to create it thus (as you are with the methods):

this.version = "0.1";

You should do the same for the CID field. Better yet, add them to the prototype object of your class.

Ishmael
+1  A: 

Cleand up your Container constructor a bit. The version and CID variables are private and within the Container constructor scope, so you do not need the this scope reference, and it would not work at all. this. reference would be needed for public accessible properties and methods, and extremely useful when you define the prototype outside of the constructor function, as shown in the second code block.

function Container() {
  var version = "0.1", CID = false;

  this.getVersion = function()      { return version };
  this.getClient  = function()      { return CID     };
  this.setClient  = function(value) { CID = value    };

  this.track = function() {
    if (CID) {
      var qs = "", data = {
        cid: this.getClient(),
        sw: screen.width ? screen.width: false,
        sh: screen.height ? screen.height: false,
        d: (new Date).getTime()
      };
      for (var p in data) qs += p +"~"+ data[p] +"-";
      var js = document.createElement("script");
      js.type = "text/javascript";
      js.src = "http://example.org/"+ qs +"version-"+ this.getVersion();
      document.getElementsByTagName("head")[0].appendChild(js);
    } else {
      alert("No Client ID");
    }
  };
};

this. reference becomes crucial when you are adding/overriding the prototype after the constructor.

function Container2() { }
Container2.prototype = {
  CID: null,
  version: "0.1",
  track: function() {
    alert(this.version);
  }
}
Mister Lucky
Thank you very much. I was clearly labouring under the false impression that i needed to create a closure or back-reference of some kind. I think it's back to the o'reilly books for me
WibblePoop
No problem, it takes a bit to wrap your head around it.And as mentioned in another post here, http://www.crockford.com/javascript/private.html is a great read to help solidify how it works.
Mister Lucky
A: 

The simple answer is to use

this.getVersion = function() { return version; }

Because the JavaScript functions are closures the reference to version, a local variable, in the function above can be accessed even after the function returns. Trying to access *this.version is an attempt to read the version member of the *this object. Since you never assigned *this a version member, it will return a value of undefined.

In Javascript, you'll only be able to access members that are either explicitly added to the object you're working with, or added to that object's prototype, or the prototype's prototype, etc.

More info on using private members with JavaScript can be found in a great article by Douglas Crockford: Private Members in JavaScript

Peter Dolberg