What is “this”?

26th July 2005

While experimenting using XMLHttpRequest I noticed a few slightly strange differences between the different browsers I was testing. These differences related to the value of the “this” keyword in the onreadystatechange event handler.

When I began writing my code using Opera I was writing my onreadystatechange handler like this,

xmlhttp.onreadystatechange = function() {
    if (this.readyState != 4) {
        return;
    }
    // do stuff
};

To me this seemed perfectly reasonable — this would be a reference to the object that had the event handler attached to it. Peter-Paul Koch has written a good piece about the this keyword in event handlers on QuirksMode.

It wasn’t until I got round to testing in some other browsers where my code didn’t work that came to the conclusion that there was something wrong with my assumption. After a little more investigation it appears each of the browsers Internet Explorer, Mozilla and Opera give the this keyword a different value within the event handler. In Internet Explorer it is the window object and in Mozilla it is a reference to the function being called. So only Opera was returning the value I was expecting when I was checking this.readyState (or indeed when I was using any other property of my xmlhttp object).

var xmlhttp;
if (window.XMLHttpRequest) {
    try {
        xmlhttp = new XMLHttpRequest();
    } catch (e) {
        xmlhttp = false;
    }
} else if (window.ActiveXObject) {
    try {
       xmlhttp = new ActiveXObject('Msxml2.XMLHTTP');
    } catch (e) {
        try {
            xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
        } catch (e) {
            xmlhttp = false;
        }
    }
}

function receiveXML() {
    if (xmlhttp.readyState != 4) {
        return;
    }

    if (this === window) {
        alert('This is IE');
    }
    if (this === xmlhttp) {
        alert('This is Opera');
    }
    if (this === receiveXML) {
        alert('This is Mozilla');
    }
}

if (xmlhttp) {
    xmlhttp.open('GET', 'test.php', true);
    xmlhttp.onreadystatechange = receiveXML;
    xmlhttp.send(null);
}

The Opera version makes far more sense to me than the other ones. It is also the only version where this seems to be of any use (or it would be useful if it worked in any other browsers ;-)) Interestingly Opera is also the only one of these browsers that let’s you define the onreadystatechange event handler once at the beginning of a script and make multiple requests to the server. Both IE and Mozilla seem to require the event handler to be redefined each time a request is made.

Permalink. Posted on 26th July 2005 in Browsers, JavaScript.

Comments

  1. I disagree. For all functions defined as this

    function foo() {
      // this == window
    }

    This should be window. This is semantically the same as doing this:

    window.foo = function() {
      // this == window
    }

    However, I believe that if you do this, then the 'this' should refer to the XML object.

    xmlhttp.onreadystatechange = function() {
      //  this == xmlhttp
    }

    # Posted by Alex on 13th September 2005.

  2. Yes, if you use

    function foo() {
        alert(this==window);  
    }
    foo();

    then within the function this==window should be true, however when you assign the function as a method of some other object,

    someObj.someEvent = foo;

    then within the function this should, I think, now reference someObj. for example if you’re working with an onclick event,

    <html>
    <head>
    <script type="text/javascript">
    function foo() {
        alert(this === document.getElementById('a'));
    }
    
    function init() {
        document.getElementById('a').onclick = foo;
    }
    
    onload = init;
    </script>
    </head>
    <body>
    <p id="a">Click me</p>
    </body>
    </html>

    The alert is true in IE, Opera and Firefox.

    Also if you’re using XMLHttpRequest the value of this doesn’t seem to be any different whether you use xmlhttp.onreadystatechange = function() { }; or xmlhttp.onreadystatechange = somefunction;

    # Posted by Andy on 14th September 2005.

  3. I believe this should refer to XMLHttpRequest only if XMLHttpRequest object had this statement in its constructor:

    this.onreadystatechange = receiveXML;
    

    Otherwise, this should refer to window. Here's a corker that illustrates what I am saying:

    anchorOne.onload = ShowFuzzyWuzzies;
    anchorTwo.onload = ShowFuzzyWuzzies;
    

    Hopefully, you're not arguing that ShowFuzzyWuzzies's this should refer to anchorTwo.

    # Posted by Dimitri Glazkov on 14th September 2005.

  4. Hi,

    anchorOne.onload = ShowFuzzyWuzzies;
    anchorTwo.onload = ShowFuzzyWuzzies;

    Hopefully, you're not arguing that ShowFuzzyWuzzies's this should refer to anchorTwo.

    Originally posted by Dimitri Glazkov.

    I guess that depends ;-) when anchorOne triggers the onload event I think ShowFuzzyWuzzies this should be anchorOne - when anchorTwo triggers the onload within ShowFuzzyWuzzies this should be anchorTwo.

    <html>
    <head>
    <title>Img load</title>
    <script type="text/javascript">
    function showFuzzyWuzzies() {
        alert("this.id = " + this.id + ", this.src = " + this.src);
    }
    </script>
    
    </head>
    <body>
    
    <img src="a.jpg" id="img1">
    <img src="b.jpg" id="img2">
    
    
    <script type="text/javascript">
    document.getElementById('img1').onload = showFuzzyWuzzies;
    document.getElementById('img2').onload = showFuzzyWuzzies;
    </script>
    
    </body>
    </html>

    # Posted by Andy on 15th September 2005.

  5. Some further testing… Mozilla/Firefox can be made to make this reference the instance of the XMLHttpRequest object with a little bit of trickery

    function createDelegate(obj, method) {
        return function() {
            return obj[method].apply(obj, arguments);
        }
    };
    // assumes xmlhttp is an instance of XMLHttpRequest 
    xmlhttp.handler = function() {
        if (xmlhttp.readyState != 4) return;
        alert('inside the onreadystatechange handler what does "this" refer to?\nit\'s the xmlhttp object: ' + (this === xmlhttp) + 'nit's the window: ' + (this === window) + '\nit\'s the function itself: ' + (this === arguments.callee));
    };
    xmlhttp.onreadystatechange = createDelegate(xmlhttp, 'handler');
    xmlhttp.open('GET', 'test.xml', true);
    xmlhttp.send(null);

    However this now totally breaks in IE, I guess I should just give up trying to make this work cross-browser…

    # Posted by Andy on 24th February 2006.

Sorry, comments for this item are currently closed.

Of Interest

Hangouts

Listening