18

This is my code:

request_xml: function()
        {
        http_request = false;
                    http_request = new XMLHttpRequest();
                     if (http_request.overrideMimeType) 
                            {
                            http_request.overrideMimeType('text/xml');
                            }
                          if (!http_request)
                          {
                                return false;
                          }
                        http_request.onreadystatechange = this.response_xml;
                        http_request.open('GET', realXmlUrl, true);
                        http_request.send(null);
                        xmlDoc = http_request.responseXML;
                        
},



response_xml:function ()
    {
        if (http_request.readyState == 4)
        {
            if(http_request.status == 404 && countXmlUrl<=3)
            {
                countXmlUrl++;
                
                realXmlUrl = xmlUrl[countXmlUrl];
                this.request_xml();
            }
            if (http_request.status == 200)
            {
                xmlDoc = http_request.responseXML;
                alert("need to update3");
                this.peter_save_data();
            }
            
        }
    },

peter_save_data:function()
    {
// removed function code
},

Strangely, the alert fires without a problem but the function call underneath gives me this error:

Error: this.peter_save_data is not a function

Calling the same function from another function elsewhere works fine.

6
  • 4
    I would imagine it's because you're using this in the wrong scope. Have you tried passing it to the peter_save_data() function but calling it like objWhatever.peter_save_data(this)? Commented Jun 11, 2011 at 18:49
  • That's not the full code. One important piece of the puzzle that is missing is how response_xml is being called -- this is important, because it will change what this is (see Jared's comment). Remember that this can be thought of as "the receiver of the method call". Commented Jun 11, 2011 at 18:52
  • Why not call it with its qualified name? Commented Jun 11, 2011 at 18:52
  • 1
    @anirudh4444 Imagine the above code comes from a prototype declaration. Thus this is the most general form that can be used. Commented Jun 11, 2011 at 18:53
  • Ok, edited the post for clarity... I have no problems pasting the full code, but its a large file... Commented Jun 11, 2011 at 18:57

3 Answers 3

30

You could do this, right before you call the XML generation.

var that = this;

and later...

that.peter_save_data();

Because this frequently changes when changing scope by using a new function, you can't access the original value by using it. Aliasing it to that allows you still to access the original value of this.

Sign up to request clarification or add additional context in comments.

3 Comments

I would consider that a potentially unnecessary global variable, which could be difficult to debug (for instance, if it is already used by something else or set elsewhere and not cleared out when the method is complete).
That variable does not need to be global. It needs to be declared locally, only before the context changes.
Thanks so much this fix my problem!
6

One important piece of the puzzle that is missing is how response_xml is being called. This is important, because it will change what this is (see Jared's comment).

Remember that this can be thought of as (roughly) "the receiver of the method call". If response_xml is passed directly to use as a callback then of course it won't work -- this will likely be window.

Consider these:

var x = {f: function () { return this }}
var g = x.f
x.f() === x    // true
g() === x      // false
g() === window // true

Happy coding.


The "fix" is likely just to change how response_xml is being called. There are numerous ways to do this (generally with a closure).

Examples:

// Use a closure to keep he object upon which to explicitly invoke the method
// inside response_xml "this" will be "that",
// which was "this" of the current scope
http_request.onreadystatechange = (function (that) {
   return function () { return that.response_xml() }
}(this)

// Or, alternatively,
// capture the current "this" as a closed-over variable...
// (assumes this is in a function: var in global context does not create a lexical)
var self = this
http_request.onreadystatechange = function () {
   // ...and invoke the method upon it
   return self.response_xml()
}

Personally, I would just use jQuery or similar ;-)

3 Comments

@Ryan Updated answer for a fix ;-)
I voted up your comment, but for a newb like me its a bit hard to understand. Although both solutions are working i am using the below one (simpler in my head) so i have accepted that one as the answer. Hope you dont mind, wish i could pick two answers here, and thanks!
@Ryan It's okay :-) Hopefully my examples were a bit helpful and your problem is solved. The important thing is to keep in mind that methods in JavaScript are not bound to an object -- they are just normal functions! The this is only correctly propagated through with how they are invoked. (It can be altered with call or apply or changing the "receiver" as shown).
-1

If you want a class-like behavior, use the right syntax, The libraries that use that, are using JSON to pass a parameter to a function that makes a class out of it.

function MyClass(CTOR paarams){
    var response_xml=function ()
    {
        if (http_request.readyState == 4)
        {
            if(http_request.status == 404 && countXmlUrl<=3)
            {
                countXmlUrl++;

                realXmlUrl = xmlUrl[countXmlUrl];
                this.request_xml();
            }
            if (http_request.status == 200)
            {
                xmlDoc = http_request.responseXML;
                alert("need to update3");
                this.peter_save_data();
            }

        }
    }

    var peter_save_data=function()
    {
       // removed function code
    }
}

var Test = new MyClass(somthing,another_something);
Test.response_xml();
//etc etc.

Or, use the libraries like Mootools where you can do it as JSON:

var T = new Class({
    response_xml:function ()
    {
        if (http_request.readyState == 4)
        {
            if(http_request.status == 404 && countXmlUrl<=3)
            {
                countXmlUrl++;

                realXmlUrl = xmlUrl[countXmlUrl];
                this.request_xml();
            }
            if (http_request.status == 200)
            {
                xmlDoc = http_request.responseXML;
                alert("need to update3");
                this.peter_save_data();
            }

        }
    },

    peter_save_data:function()
    {
      // removed function code
    }

});
var X = new T();//etc etc

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.