SitePoint Sponsor

User Tag List

Results 1 to 6 of 6
  1. #1
    SitePoint Member
    Join Date
    Dec 2006
    Posts
    3
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Custom object method losing scope?

    I am new to making my own javascript objects (just started yesterday). It seems that when this.response is called by the XMLHttpRequest object it loses scope.

    This confuses me in that at least with C++, Java, and what not when objects are created the methods and members are assigned their own place in memory, unless they are static. If that is also true in javascript, when I pass the function address (this.response) to onreadystatechange, it should not lose scope.

    I tried doing some debugging by tracing the process in which this.response was called (used this.caller), and the alert box showed me 'function open(){[native code]}'. I then tried 'this.caller.caller' and that took me back to my request method. That is the farthest back I can go. There doesn't seem to be a parent property that I can use.

    Any ideas as to what is going on behind the scenes? Any ideas on how to call response while keeping the object.

    To note: the reason that I am creating the object is so that I can receive multiple types of data and pass them along to functions that are made to manipulate that data.

    The code is as follows:

    Code:
    function httpRequest(method,url,send,respond) {
    	this.method = method;
    	this.url = url;
    	this.send = send;
    	this.respond = respond;
    }
    httpRequest.prototype = {
    	method: "", //post, get
    	url: "", //url to send to
    	send: "", //data we want to send
    	respond: function() {}, //function you want called with data sent by server
    	requestObj: new XMLHttpRequest(),
    
    	request: function()  { //XMLHttpRequest
    		if(isMSIE) { // Internet Explorer
    			this.requestObj = new ActiveXObject('Microsoft.XMLHTTP');
    		}
    
    		this.requestObj.onreadystatechange = this.response;
    		this.requestObj.open(this.method, this.url);
    		this.requestObj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    		this.requestObj.send(this.send);
    	},
    
    	response: function() { //callback function for response from server
    		if(this.requestObj.readyState == 4) {
    			if(this.requestObj.status == 200) {
    				this.respond(this.requestObj.responseText);
    			}
    		}
    	}
    };

  2. #2
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Pat, Welcome to SPF!

    Sorry for the late reply. Did you find your answers or do you still have some questions about this?

  3. #3
    SitePoint Member
    Join Date
    Dec 2006
    Posts
    3
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    As for the welcome to SPF, thanks. I have been here at SPF as the user Ghandi since 2002, but as I no longer go by that name, anywhere, I have opted to create a new account and use my real name.

    I have pretty much figured out that the method does lose scope of where it was. Based on a video of Douglas Crockford talking about advanced javascript it seems that methods are "static" in that there is only one version in memory.

    It seems that a method inherits from the object it is in. If it can not find the member or method in its own 'this', it then looks into its inherited 'this' for the member or method in question. Douglas Crockford discusses this in his 'Advanced Javascript' presentation

    When the method is passed into the onreadystatechange, it is called from a function that looks like 'function open() { [native code] }'. This, of course, does not say much, except that it was not called from within the object it is defined in, which is the reason why I think it behaves in the way I said above.

    To get to the point, the method being called from outside of the scope of its parent object (not this.response or request.response) prevents it from getting at sibling members and methods.

    What I would really like to know is if this is the intended behavior or if it just really has never come up before and thus never fixed. I guess since there is only one version of any method for an object that this would be the intended behavior. Given my upbringing in C++ I assumed it was a function address for that specific method in the created object, which should have enabled me to use the response inside the object. Given my findings, I have opted for the version below.

    Code:
    function httpRequest(method,url,send,respond) {
    	this.method = method;
    	this.url = url;
    	this.send = send;
    	this.respond = respond;
    }
    httpRequest.prototype = {
    	requestObj: new XMLHttpRequest(),
    
    	request: function()  { //XMLHttpRequest
    		if(isMSIE) { // Internet Explorer
    			this.requestObj = new ActiveXObject('Microsoft.XMLHTTP');
    		}
    
    		rO = this.requestObj;
    		res = this.respond;
    
    		this.requestObj.onreadystatechange = function() { alert(this.caller); response(rO, res); };
    		this.requestObj.open(this.method, this.url);
    		this.requestObj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    		this.requestObj.send(this.send);
    	}
    };
    function response(requestObj, respond) { //callback function for response from server
    	if(requestObj.readyState == 4) {
    		if(requestObj.status == 200) {
    			respond(requestObj.responseText);
    		}
    	}
    }
    Last edited by PatKohler; Dec 8, 2006 at 10:57. Reason: forgot something

  4. #4
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Pat,

    Code:
    function Widget() // object prototype
    {
      this.id = '';
      this.myfunc = function()
      {
      };  
      setTimeout(this.myfunc, 1000);
    }
    In the above code it appears that we want "this.myfunc" to be called after 1 second. And it does indeed get called after 1 sec. However, when it is called the "this" variable does not point to the proper object instance as we might think it should. It probably will point to the window object.

    When we pass "this.myfunc" as the first argument to setTimeout, we are only passing a reference to the function. There is no information passed that would associate myfunc with "this" instance object.

    Before the Javascript interpreter "calls" any function, it first sets the value of the "this" variable. What does it get set it to? For example:
    Code:
    // If the calling expression is like this:
    myobj.func();
    // then "this" will be assigned a reference to "myobj" before "func" is called.
    
    // If the calling expression is like this:
    func();
    // then "this" will be assigned a reference to the "window" object (the "global" object) before "func" is called.
    Named functions and variables don't exist on their own - they are always a property or method of some object.
    Code:
    // we tend to call this a "global" variable
    // because it is declared outside of any functions
    var myvar = 1;
    // but it is actually a property of the window object.
    // And the same goes for functions:
    function myfunction()
    {
    }
    // "myfunction" is actually a method of the window object.
    I'll post an httpRequest object I've been playing around with. I'm not just trying to get you to use it (in fact, it is just an experiment) - I just think it will illustrate some good points and probably make you think of some questions.

    Code:
    // This implementation is based loosely on XHConn (http://xkr.us/code/javascript/XHConn/)
    
    function xHttpTransport() // object prototype
    {
      // Public Properties
      this.busy = false;
      this.count = 0;
      this.err = {name:'',message:''};
      // Private Properties
      var _me = this;
      var _ro = null; // http request object
      var _tm = null; // timer
      var _cb = null; // callback function
      // Private Event Listeners
      function onchange()
      {
        if (_ro.readyState == 4) {
          clearTimeout(_tm);
          _me.busy = false;
          _cb(_ro);
        }
      }
      function ontimeout()
      {
        _me.err.name = 'Timeout';
        _me.err.message = 'Timeout on request ' + _me.count;
        _ro.abort();
        _me.busy = false;
        _cb(null);
      }
      // Public Method
      this.request = function(sMethod, sUrl, sData, uTimeout, fnCallback)
      {
        if (this.busy || !_ro) { return false; }
        sMethod = sMethod.toUpperCase();
        ++this.count;
        try {
          if (sMethod == 'GET') {
            _ro.open(sMethod, sUrl + '?' + sData, true);
            sData = '';
          }
          else {
            _ro.open(sMethod, sUrl, true);
            _ro.setRequestHeader('Method', 'POST ' + sUrl + '?' + this.count + ' HTTP/1.1');
            _ro.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
          }
          _ro.onreadystatechange = onchange;
          _ro.send(sData);
        }
        catch(e) {
          this.err.name = e.name;
          this.err.message = e.message;
          return false;
        }
        this.busy = true;
        _cb = fnCallback;
        this.err.name = this.err.message = '';
        _tm = setTimeout(ontimeout, uTimeout);
        return true;
      };
      // Constructor Code
      try { _ro = new XMLHttpRequest(); }
      catch (e) { try { _ro = new ActiveXObject('Msxml2.XMLHTTP'); }
      catch (e) { try { _ro = new ActiveXObject('Microsoft.XMLHTTP'); }
      catch (e) { _ro = null; }}}
      if (!_ro) return null;
    }

  5. #5
    SitePoint Member
    Join Date
    Dec 2006
    Posts
    3
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    -'this' means public & 'var', 'function' are private
    -private members and methods can be accessed nicely within the public method

    That is information I have not seen before. Other than that I really don't see anything else that may have drawn questions.

  6. #6
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Note that they are accessible even in the event listeners. They are accessible because of the "closure" that is formed - which is the answer to your original question.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •