Nate, beautiful!

the tricky:
 obj2.run.call(instance); // now obj2.run is called, it's own 'this' points
to obj instead of obj2

shows that this is not always what is seems to be! call and apply are both
ways to override this.

Because they are tricky... I like to isolate call and apply to the bowels of
my code, no where near the main code. I'd bet 95% of JavaScript programmers
don't know how to use call and apply.


On 6/24/07, Nate Cavanaugh <[EMAIL PROTECTED]> wrote:



Hi Glenn,
Closures are a pretty tough concept to fully explain. In this case, I
should
have just said to use an "anonymous function", as it would probably lead
to
less confusion.
Here is a great online resource about closures that really helped me a
lot:
http://www.hunlock.com/blogs/Closing_The_Book_On_Javascript_Closures

About the mechanism of this:

This always points to the current object that it's running in, except in
two
cases: when it's pointing to the element that executed an action, or it is
explicitly set to something different.

I'll give you a good example:

var obj = {
        init: function() {
                var instance = this; // points to obj

                var obj2 = {
                        run: function() {
                                var instance = this; // points to obj2
                        }
                };

                $('a').click(
                        function(e) {
                                var item = this; // points to the element
that was clicked
                        }
                );

                obj2.run.call(instance); // now obj2.run is called, it's
own 'this' points
to obj instead of obj2
        }
};

That piece of code covers the different situations I was talking about.

The 'call' method explicitly overrides the this in obj2 with a new obj,
and
'this' now points to the object passed in.

But in every other case, this points to the current object that it's
existing in.

By the way, to limit confusion a bit, your code, even though it was
functions, in Javascript those functions are objects as well, so the
scoping
applies.

I hope that helps clear things up, but if not, feel free to ask away :)


knnelg wrote:
>
>
> Hi Nate,
>
> First of all, thank you very much for taking the time to respond.  It's
> greatly appreciated.
>
> I had to modify your solution slightly, in that the ajax.complete
> callback passes two parameters which I need, so I changed the code to...
>
> var instance = this;
>
> $.ajax(
>
>     {
>         type: "POST", // type of http request
>         url: this.sourceURL, // where to get file from
>         dataType: "xml", // type of file to retrieve
>         complete: function(ajaxResponse, status){
>             instance.parseXML(ajaxResponse, status);
>         }
>     }
> );
>
> This works just fine, and I thank you once again.  I'd never have worked
> this out using the books I have.
>
> I have to admit though, I still don't fully understand the mechanism
> going on here.  If anyone is aware of any on-line resources that go into
> detail about the scope of 'this', I'd be grateful.
>
> You mention the phrase "you can use a closure", is this a common term in
> Javascript? What does it mean?
>
> Once again, thank you.
>
> Regards,
>
> Glenn
>
>
>
> Nate Cavanaugh wrote:
>> Hi Glenn,
>> Keeping scope is definitely one of the more frustrating aspects of
>> Javascript sometimes.
>>
>> What's happening is that you're running the ajax call, and the complete
>> method is a method of the object that you're passing into the ajax
>> handler.
>>
>> So this now points to the object containing the properties you sent in.
>>
>> To do this so that your member method (the this.parseXML) retains the
>> proper
>> scope, you can use a closure. I also like to always start off my
methods
>> with a var declaration pointing to the instance this (so I always know
>> when
>> I am pointing to the instance, and when I use this, that I am pointing
to
>> an
>> element, such as in an event call back):
>>
>> var instance = this;
>>
>> $.ajax(
>> {
>> type: "POST", // type of http request
>> url: this.sourceURL, // where to get file from
>> dataType: "xml", // type of file to retrieve
>> complete: function(){
>> instance.parseXML();
>> }
>> }
>> );
>>
>> Now your parseXML method will run in the proper scope.
>>
>> I hope that helps :)
>>
>>
>> knnelg wrote:
>>
>>> Hi,  I'm another newbie to jQuery and also not that experienced in
>>> JavaScript so I'm not sure if my issue is jQuery or JavaScript.
>>>
>>> The issue is that I'm creating a constructor for an object, in which I
>>> initialise some instance properties, then kick off an asynchronous
>>> $.ajax event to retrieve an XML file.  The callback method of
>>> the .ajax object is bound to a prototype method of the same object as
>>> the constructor.
>>>
>>> My problem is that when the ajax call is complete and the callback
>>> method is called, it appears that the callback method cannot see any
>>> of the 'this'.properties that were initialized in the constructor.
>>>
>>> I'm beginning to think that the asynchronous nature of the ajax object
>>> has actually created a new instance of my object in which no
>>> constructor was called and therefore is not working against the
>>> original instance I created.  But then again I always look for the
>>> most obscure explanation instead of noticing the simple error I've
>>> made.
>>>
>>> I've included the code below.  If anyone can show me the errors of my
>>> ways, I'd be very grateful.
>>>
>>> The line causing me a problem is commented with // THIS LINE FAILS
>>> WITH AN "this.woffers has no properties" ERROR
>>>
>>> ================================================================
>>> // Use jQuery 'document.ready' selector to define the function to call
>>> when page is ready.
>>> $(main);
>>>
>>>
//////////////////////////////////////////////////////////////////////////////
>>> // The main entry function hopefully called by the jQuery 'ready'
>>> selector
>>>
//////////////////////////////////////////////////////////////////////////////
>>> function main() {
>>>
>>>     try {
>>>             //create a new 'Woffers' instance
>>>             var woffers = new Woffers("woffersdata.xml", "xml");
>>>     }
>>>
>>>     // Catch any exceptions...
>>>     catch(e) {
>>>
>>>             // ...and report them.  TODO: remove this for live
>>>             alert(e);
>>>
>>>     }
>>>
>>> }
>>>
>>>
//////////////////////////////////////////////////////////////////////////////
>>> // Constructor for a 'Woffers' class which will contain all our
>>> 'Woffer' items
>>> // and some methods to retrieve them.
>>> //
>>> // Expects:
>>> //  sourceURL - a String containing the location of the file
containing
>>> the
>>> //          data to be used to construct our object.
>>> //  sourceType - a String containing the type of file we are using to
>>> //          construct the object.  Can be "xml" or "json"
>>>
//////////////////////////////////////////////////////////////////////////////
>>> function Woffers(sourceURL, sourceType) {
>>>     if(sourceType != "xml") {
>>>             throw new Error("Invalid source type (" + sourceType + ")
supplied
>>> to Woffers constructor.");
>>>     }
>>>
>>>     // Store parameters for later use by any method
>>>     this.sourceURL = sourceURL;
>>>     this.sourceType = sourceType;
>>>
>>>     // Create a property to hold a list of Woffer objects
>>>     this.woffers = new Array(); // <-- This property should get
populated
>>> by the $.ajax callback method
>>>
>>>     switch(this.sourceType) {
>>>             case "xml": {
>>>                     $.ajax(
>>>                             {
>>>                                     type: "POST", // type of http
request
>>>                                     url: this.sourceURL, // where to
get file from
>>>                                     dataType: "xml", // type of file
to retrieve
>>>                                     complete: this.parseXML //
Function to run on completion which
>>> will populate this.woffers
>>>                             }
>>>                     )
>>>
>>>                     break;
>>>
>>>             } // End case(xml)
>>>
>>>             case "json": {
>>>                     $.ajax(
>>>                             {
>>>                                     type: "POST", // type of http
request
>>>                                     url: this.sourceURL, // where to
get file from
>>>                                     dataType: "json", // type of file
to retrieve
>>>                                     complete: this.parseJson //
Function to run on completion
>>>                             }
>>>                     )
>>>
>>>                     break;
>>>
>>>             } // End case(json)
>>>     }
>>>
>>> } // End Woffers constructor
>>>
>>>
//////////////////////////////////////////////////////////////////////////////
>>> //
>>>
//////////////////////////////////////////////////////////////////////////////
>>> Woffers.prototype.parseXML = function(woffersDOM, resultStatus) {
>>>     if(resultStatus != "success") {
>>>             throw new Error("Encountered a problem retreiving the XML
file." +
>>>                                             " Reported status is " +
ResultStatus + ".");
>>>     }
>>>     // retrieve a list of woffers from the response
>>>     var retrievedWoffers =
>>> woffersDOM.responseXML.documentElement.getElementsByTagName("route");
>>>
>>>     // Go through each woffer and use it to create a new 'Woffer'
object
>>>     for(var index = 0; index < retrievedWoffers.length; ++index) {
>>>
>>>         //
>>>         // THIS LINE FAILS WITH AN "this.woffers has no properties"
>>> ERROR
>>>         //
>>>         // Create a new woffer and add it to 'this.woffers'
>>>         this.woffers.push(new Object());
>>>
>>>     }
>>>
>>> }
>>>
>>>
>>> Any feedback, much appreciated
>>>
>>> Regards,
>>>
>>> Glenn
>>>
>>>
>>>
>>>
>>
>>
>
>

--
View this message in context:
http://www.nabble.com/Loosing-access-to-%27this%27-objects-in-callback-method%21-tf3970581s15494.html#a11277167
Sent from the JQuery mailing list archive at Nabble.com.




--
Ⓙⓐⓚⓔ - יעקב   ʝǡǩȩ   ᎫᎪᏦᎬ

Reply via email to