Understanding the JavaScript “this” keyword: Part 2

In the previous article, we uncovered the fundamental rules around how the value of this is determined when inside a function. If you haven’t read it yet, head over to the first article, and pop back here when you’re done.

From this point onwards, we’re going to explore a number of JavaScript features that throw those rules out of the window, and allow the caller to choose the value of this.

  1. Changing the value of this using call() and apply()We know if you call someObject.foo() that this inside foo will be someObject. What if we want it to be anotherObject?

    Well, we could do this;

    anotherObject.foo = someObject.foo;
    anotherObject.foo();
    delete anotherObject.foo;

    Bit of a hack, no? Enter call() and apply(). All you have to do is;

    someObject.foo.call(anotherObject);

    What if you want to pass arguments to foo() as well? No problem!

    someObject.foo.call(anotherObject, 1, 2, "abc");

    apply() works exactly the same, but the difference is how they let you pass arguments to the function. call() expects each argument as a separate parameter, whereas apply() expects a single array, whose elements are all the parameters. The equivalent apply() call for the above would be;

    someObject.foo.apply(anotherObject, [1, 2, "abc"]);
  2. Forcing the value of this using bind()bind() is a tricky addition to the JavaScript language (introduced in ES5). If you call someObject.foo.bind(anotherObject), bind() will return a new function; and no matter how you call the returned function (even using call() and apply()), the value of this will always be anotherObject.
    var obj = {
        val: 4
    };
    
    function foo() {
        alert(this.val);
    }
    
    var bar = foo.bind(obj);
    
    foo(); // behaves as expected; alerts `undefined` as `foo` is not attached to an object
    bar(); // breaks the rules! alerts `4` because `bind()` sets `this` to `obj`.
    
    bar.call(window); // this has no effect; still alerts `4`.

    Try it yourself!

    You might wonder how bind() works; but even if ES5 hadn’t included bind(); well, we could roll out our own really easily;

    Function.prototype.ourBind = function(thisArg) {
        var that = this;
    
        return function() {
            return that.apply(thisArg, arguments);
        };
    };

    We could then use ourBind() exactly how we’d use bind(); see it in action on jsFiddle!

  3. The new keyword has the last say…One last feature in JavaScript that I’d like to mention before finishing this article is the new keyword.

    If you’re not quite clued up on the behaviour of new you can read more about it here.

    Using new when calling a function is the final way to change the value of this when calling a function. When you use new, this points to a new object, whose “prototype” is set to the prototype of the function you called new on. Yep; that might take a few reads to sink in.

    The only use case for this is when creating instances of a object; but I just wanted to complete the list of “ways-this-can-be-changed”.

That concludes the articles on this; I hope you now have a much better understanding about how it works! If you want to read a few more articles to help it sink in, I’d recommend this quirksmode article and this Stack Overflow question

Understanding the JavaScript “this” keyword: Part 1

This article is the first of two articles which explain the this keyword in JavaScript. In this first post, we’ll look at what the value of this will be under various invocation scenarios. In the second article, we’ll look at how these rules can be bent by the caller.

Before we begin, I’d like to go over two JavaScript concepts. It’s crucial these are understood to be able to grasp the behaviour of this:

  1. The value of this within a function is not resolved until the function is called.This means that one function can be called 10 different times, and each time a different value of this could be observed.
  2. Whenever you name a function in JavaScript, what you actually do is create a named variable which references that function (i.e. the named variable points to the function). When you assign that named variable to another variable (i.e. alias the function), what you actually do is pass that reference by value, which leaves both variables pointing to the same function.
    // "foo" points to a function which alerts "hi"
    function foo() {
        alert("Hi");  
    }
    
    // Set "b" to point to the same function as "foo" points to (which alerts "hi").
    var b = foo;
    
    // "bar" points to a function which accepts a parameter which points to a function.
    // The body of the function simply executes the function provided.
    function bar(f) {
        f();   
    }
    
    // Pass the function pointed to be "foo" to the function pointed to by "bar".
    bar(foo);

    This means you can have one function, which can be pointed to by multiple variables. If this is still unclear, perhaps the answers to this Stack Overflow question will help the point sink in.

Once you’ve understood the two concepts above, the rules as to what the value of this is is quite straightforward; it’s the rule-bending we discuss in the next article that makes everything more more complicated!

  1. If you access the function via a property of an object, the value of this will be the object.
    var obj = {
        foo: function () {
            alert(this.bar); 
        },    
        bar: 4
    };
    
    obj.foo(); // alerts "4", as `this` inside `foo()` is `obj`.
    
  2. If the variable you access the function from is just a normal variable, the value of this depends on whether you’re in strict mode. Unless you know otherwise, chances are you won’t be.
    • If you are in strict mode, this will be undefined
    • If you aren’t in strict mode, this will the global object. In browsers, the global object is window.
    function foo() {
        "use strict";
        
        alert(typeof this === "undefined");
    }
    
    function bar() {
        alert(typeof this === "object" && this === window);
    }
    
    foo();  // alerts `true`, as inside strict mode, `this` is undefined
    bar();  // alerts `true`, as inside non-strict mode, this is the global (window) object
    

That’s all there is to it! One common “got-cha” however is when you pass a function (which was pointed to by a object member) as a parameter to another function, people still expect this to be the object.

// Define an object which has a property "foo", which points to a function.
// that function alerts "this.val".
var obj = {
    foo: function () {
        alert(this.val);   
    },

    val: "bar"
};

// Remember, if the function we're calling is a member of an object, the value of `this` will be the object.
// In this case "this" resolves to "obj", so we see "bar" being alerted.
obj.foo();

// Lets define another function, which accepts a function as a parameter. All it does is call that function.
function baz(f) {
    f(); 
}

// Now we pass the function pointed to by "obj.foo" to "baz()". "this" will now be the global object, and
// "this.val" will be undefined.
baz(obj.foo);

No! Don’t forget when you alias a function (either by assigning it to another variable or passing it as a parameter to another function), a new pointer to the same function is created. That new pointer has it’s own behaviour; and it’ll follow behaviour #2.

With the rules out of the way, lets go through some examples to see the rules being applied in practise;

function foo() {
    console.log(this);   
}

foo(); // This will be the global object ("window")

var obj = {
    bar: foo  
};

obj.bar(); // This will be "obj"

var baz = obj.bar;

baz(); // This will be the global object ("window") again.

The use of literals vs constructors (new Array vs [] and new Object vs {})

In JavaScript there are two main ways to declare a new object or a new array. One uses literal syntax whilst the other favours the more verbose method of calling the constructor.

var obj = {};
var ar = [];

… or…

var obj = new Object;
var ar = new Array;

Over the years using literal syntax has become the idiomatic way due to the following reasons (and it is because of these reasons that you should also be using them):

  1. The names Object and Array can be overridden, whilst the literal equivalents cannot:

    Array = Object = function () {
        alert('Foo');
    };
    
    var ar = new Array; // alerts "Foo"
    var obj = new Object; // alerts "Foo"
    

    Whilst you may think “I’m never going to override Object or Array so why should I care”, remember there’s nothing to stop 3rd party libraries you include doing this; quite possibly accidentally, but it’ll still break your program.

  2. The object literal allows you to define attributes inline. You cannot do this with the constructor counterpart.

    var obj = {
        prop: 1,
        add: function (val) {
            return this.prop + val;
        }
    };
    
  3. The array constructor has a horrible interface which changes depending on the situation.

    // What do you think ar and ar2 equal?
    var ar = new Array(1);
    var ar2 = new Array(1,2,3,4);
    
    // Probably this?
    alert(ar.join(",") === "1");
    alert(ar2.join(",") === "1,2,3,4");
    

    …. In fact passing one parameter to new Array creates an array whose length is the value passed to the first parameter. The value of each of these elements are undefined. On the other hand passing more than one parameter creates an array whose elements are equal to each of these parameters.

The risk of JSON hijacking

JSON hijacking is an exploit which has not had the publicity it perhaps deserves. It is a real risk to website security; as much as CSRF or XSS.

It’s easier to understand how JSON hijacking works if you have a basic understanding on the differences between JSON and JSONP.

Traditionally, to utilize a JSON response we need to make a request via XHR, retrieve the responseText, and then parse it using JSON.parse() (or similar). As XHR requests are restricted to the same origin policy (SOP), this is only relevant if we’re making the request to the same domain we’re on; and because we (usually) control that domain, there is little risk.

JSONP requests (made by inserting <script> tags) are not restricted by the SOP, but the problem here is that a JSON response normally has no effect; i.e. there’s no problems retrieving a JSON response via this method, but there’s no way to capture or utilize the response;

<script src="http://www.remote-server.com/get-json.php">
{
    "user_id": 1234,
}
</script>

However, clever folks discovered that in some browsers, you can cause an effect to happen;

  1. You can override the Array constructor to do something with the array

    Array = function () {
        for (var i=0;i<this.length;i++) {
            alert(this[i]);
        }
    }
    
  2. To access object attributes, you can define a setter to capture the setting of that attribute;

    Object.prototype.__defineSetter__("user_id", function (value) {
        alert("user_id is being set to: " + value);
    });
    

Bear in mind that neither of these exploits are cross browser. However, this is a real threat that has been used to target the likes of Google and Twitter.

So how exactly does this work?

  1. An attacker injects the above snippet(s) on a website you visit (a website he owns?)
  2. When the request for JSON from the 3rd party domain is made, any cookies you have for that domain are sent with the request; if you’ve got a session on that website, the website is non-the-wiser that it’s not actually you requesting that page.
  3. When the response is returned, the above snippets are executed, and the attacker can manipulate/ steal the response; any personal/ important details contained in there are now his.

Fortunately, there are a number of ways to fix this;

  1. Rigorously check for the X-Requested-With header to check the request came via XHR.
  2. Add breaking code to the JSON response.
    • Facebook lead the JSON response with a for (;;); (infinite loop).
    • Google lead the JSON response with a throw 1; (throw an error).

Because of these additions, when the browser evaluates the response, the object/ array declarations are never reached. Obviously in your code, you’ll need to strip the leading mush out before you attempt to parse the JSON;

var json = JSON.parse(this.responseText.slice("for (;;;);".length));

or

var json = JSON.parse(this.responseText.slice("throw 1;".length));

For further reading, you may be interested in the following information articles; JSON Hijacking on http://haacked.com, JSON Hijacking on http://thespanner.co.uk.

Checking for null or undefined, and introducing typeof === undefined

It can be difficult to decide whether you should be checking for null or undefined in JavaScript; on the face of things it may seem that they do the same job.

Fortunately, there’s quite an easy to follow rule with this; unless you specifically set something to null, you should most likely be checking for undefined.

As ever there are exceptions, and here it is; if using document.getElementById(), null; is returned if no element could be found.

Let’s go through some situations where things will be undefined:

  1. Checking a variables default value. An un-initialized variable will be undefined. Caveat though; if a variable is undeclared, this will throw an error.

    var foo;
    alert(foo === undefined); // true
    alert(bar === undefined); // error
    
  2. The default return value of a function.

    function foo() { } 
    function bar() { return; }
    
    alert(foo() === undefined); // true 
    alert(bar() === undefined); // true
    
  3. The value of an unspecified attribute in an Object

    var obj = {};
    alert(obj.foo === undefined); // true
    
  4. The value of an out-of-bounds array index

    var array = []; 
    alert(array[100] === undefined); // true
    

However, should you really be checking against undefined, are there any caveats of doing so? It turns out there is!

  1. As mentioned above, comparing an undeclared variable against undefined will throw a ReferenceError: foo is not defined.
  2. undefined is a property on the global object. Prior to ES5, this value was writable; allowing problems like this;

    var foo;
    undefined = 42;
    
    alert(foo === undefined); // false
    alert(42 === undefined); // true
    

To avoid these caveats, it turns out you should use the typeof operator instead, and compare against the Undefined type:

var foo;
undefined = 42;

alert(typeof foo === "undefined"); // true
alert(typeof 42 === "undefined"); // false
alert(typeof bar === "undefined"); // true- no error!