javascript – How does this keyword work within a function?

javascript – How does this keyword work within a function?

Cannibalized from another post of mine, heres more than you ever wanted to know about this.

Before I start, heres the most important thing to keep in mind about Javascript, and to repeat to yourself when it doesnt make sense. Javascript does not have classes (ES6 class is syntactic sugar). If something looks like a class, its a clever trick. Javascript has objects and functions. (thats not 100% accurate, functions are just objects, but it can sometimes be helpful to think of them as separate things)

The this variable is attached to functions. Whenever you invoke a function, this is given a certain value, depending on how you invoke the function. This is often called the invocation pattern.

There are four ways to invoke functions in javascript. You can invoke the function as a method, as a function, as a constructor, and with apply.

As a Method

A method is a function thats attached to an object

var foo = {};
foo.someMethod = function(){
    alert(this);
}

When invoked as a method, this will be bound to the object the function/method is a part of. In this example, this will be bound to foo.

As A Function

If you have a stand alone function, the this variable will be bound to the global object, almost always the window object in the context of a browser.

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

This may be whats tripping you up, but dont feel bad. Many people consider this a bad design decision. Since a callback is invoked as a function and not as a method, thats why youre seeing what appears to be inconsistent behavior.

Many people get around the problem by doing something like, um, this

var foo = {};
foo.someMethod = function (){
    var that=this;
    function bar(){
        alert(that);
    }
}

You define a variable that which points to this. Closure (a topic all its own) keeps that around, so if you call bar as a callback, it still has a reference.

NOTE: In use strict mode if used as function, this is not bound to global. (It is undefined).

As a Constructor

You can also invoke a function as a constructor. Based on the naming convention youre using (TestObject) this also may be what youre doing and is whats tripping you up.

You invoke a function as a Constructor with the new keyword.

function Foo(){
    this.confusing = hell yeah;
}
var myObject = new Foo();

When invoked as a constructor, a new Object will be created, and this will be bound to that object. Again, if you have inner functions and theyre used as callbacks, youll be invoking them as functions, and this will be bound to the global object. Use that var that = this trick/pattern.

Some people think the constructor/new keyword was a bone thrown to Java/traditional OOP programmers as a way to create something similar to classes.

With the Apply Method

Finally, every function has a method (yes, functions are objects in Javascript) named apply. Apply lets you determine what the value of this will be, and also lets you pass in an array of arguments. Heres a useless example.

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = [ah,be];
foo.apply(omg,args);

Function calls

Functions are just a type of Object.

All Function objects have call and apply methods which execute the Function object theyre called on.

When called, the first argument to these methods specifies the object which will be referenced by the this keyword during execution of the Function – if its null or undefined, the global object, window, is used for this.

Thus, calling a Function…

whereAmI = window;

function foo()
{
    return this is  + this.whereAmI +  with  + arguments.length +  + arguments;
}

…with parentheses – foo() – is equivalent to foo.call(undefined) or foo.apply(undefined), which is effectively the same as foo.call(window) or foo.apply(window).

>>> foo()
this is window with 0 arguments
>>> foo.call()
this is window with 0 arguments

Additional arguments to call are passed as the arguments to the function call, whereas a single additional argument to apply can specify the arguments for the function call as an Array-like object.

Thus, foo(1, 2, 3) is equivalent to foo.call(null, 1, 2, 3) or foo.apply(null, [1, 2, 3]).

>>> foo(1, 2, 3)
this is window with 3 arguments
>>> foo.apply(null, [1, 2, 3])
this is window with 3 arguments

If a function is a property of an object…

var obj =
{
    whereAmI: obj,
    foo: foo
};

…accessing a reference to the Function via the object and calling it with parentheses – obj.foo() – is equivalent to foo.call(obj) or foo.apply(obj).

However, functions held as properties of objects are not bound to those objects. As you can see in the definition of obj above, since Functions are just a type of Object, they can be referenced (and thus can be passed by reference to a Function call or returned by reference from a Function call). When a reference to a Function is passed, no additional information about where it was passed from is carried with it, which is why the following happens:

>>> baz = obj.foo;
>>> baz();
this is window with 0 arguments

The call to our Function reference, baz, doesnt provide any context for the call, so its effectively the same as baz.call(undefined), so this ends up referencing window. If we want baz to know that it belongs to obj, we need to somehow provide that information when baz is called, which is where the first argument to call or apply and closures come into play.

Scope chains

function bind(func, context)
{
    return function()
    {
        func.apply(context, arguments);
    };
}

When a Function is executed, it creates a new scope and has a reference to any enclosing scope. When the anonymous function is created in the above example, it has a reference to the scope it was created in, which is binds scope. This is known as a closure.

[global scope (window)] - whereAmI, foo, obj, baz
    |
    [bind scope] - func, context
        |
        [anonymous scope]

When you attempt to access a variable this scope chain is walked to find a variable with the given name – if the current scope doesnt contain the variable, you look at the next scope in the chain, and so on until you reach the global scope. When the anonymous function is returned and bind finishes executing, the anonymous function still has a reference to binds scope, so binds scope doesnt go away.

Given all the above you should now be able to understand how scope works in the following example, and why the technique for passing a function around pre-bound with a particular value of this it will have when it is called works:

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
this is obj with 2 arguments

javascript – How does this keyword work within a function?

Is this defined behavior? Is it
cross-browser safe?

Yes. And yes.

Is there any reasoning underlying why
it is the way it is…

The meaning of this is pretty simple to deduce:

  1. If this is used inside a constructor function, and the function was invoked with the new keyword, this refers to the object that will be created. this will continue to mean the object even in public methods.
  2. If this is used anywhere else, including nested protected functions, it refers to the global scope (which in the case of the browser is the window object).

The second case is obviously a design flaw, but its pretty easy to work around it by using closures.

Leave a Reply

Your email address will not be published.