Four scents of "this"

  1. First, when called as a method
  2. Second, when called as a function
  3. Third, in new
  4. Fourth, explicit this
    1. call
    2. apply
  5. Summary

The value of this is dynamic in JavaScript. It is determined when function is called, not when it is declared.

Any function may use this. It doesn’t matter if the function is assigned to the object or not.

The real value of this is evaluated in the call time anyway, and there are 4 possible cases.

First, when called as a method

If a function is called from the object (either dot or square bracket will do), this refers to this object.

var john = { 
  firstName: "John" 
}

function func() { 
  alert(this.firstName + ": hi!")
}

john.sayHi = func

john.sayHi()  // this = john

In the example above, func is initially apart from the object. But when called john.sayHi() sets this to the object before dot: john.

Second, when called as a function

If a function uses this, then it is meant to be called as a method. A simple func() call is usually a bug.

When there is no context, this becomes window:

func() 

function func() { 
  alert(this) // [object Window] or [object global] or kind of..
}

In the modern language specification, this behavior is altered, and this = undefined. By default, the browsers still behave the old (compatible) way, but some of them, like Firefox 4, support "use strict". It switches the behavior to modern standard.

Try in Firefox 4:

func()  

function func() { 
  "use strict"

  alert(this) // undefined in Firefox 4
}

The reference type

How do you think, obj.go works equally in these calls?

1. obj.go()
2. (obj.go)()
3. (a = obj.go)()
4. (0 || obj.go)()

The answer is “No”, because the two latter calls do not receive this.

How do you think, why? You can try:

obj = {
  go: function() { alert(this) }
}

obj.go(); // object

(obj.go)(); // object 

(a = obj.go)(); // window

(0 || obj.go)(); // window

Give it a thought before going ahead…

That’s because of so called Reference Type, described in the language specification.

To be passed as this, the value before brackets "value()" must be of Reference Type, or, shortly, an accessor, like obj.a or obj['a'], but not anything more complex. Brackets around an accessor do not change it, so (obj.a) also works.

Any other expression, like (a = obj.method)() or (a.method || b.method)() does not pass proper this.

What will be the result of the code below?

obj = {
  go: function() { alert(this) }
}

(0 || obj.go)()

Open solution
Solution

Error! Try it:

obj = {
  go: function() { alert(this) }
}

(0 || obj.go)()  // error

That’s because there is no semicolon after obj. JavaScript ignores spaces after obj and reads the code as:

obj = { go: ... }(0 || obj.go)()

It tries to call object {...}(..) as a function, evaluate the right part of the expression and assign the result to obj.

That’s better (semicolon added):

obj = {
  go: function() { alert(this) }
}*!*;*/!*

(0 || obj.go)()  // window

Third, in new

When a new function is called, this is initialized as a new object.

We’ve discussed that in the Object constructor section.

Fourth, explicit this

That’s the tricky and JavaScriptish part.

A function can be called with explicit this value. This is done out by one of two methods: call or apply.

call

The syntax of call:

func.call(obj, arg1, arg2,...)

The first argument of call becomes this, other arguments arg1, arg2... become arguments.

For example:

var john = { 
  firstName: "John" 
}

function func() { 
  alert( this.firstName )
}

*!*
func.call(john)  // "John"
*/!*

In the example above, func is called with this = john.

Let’s add arguments:

var john = { 
  firstName: "John",
  surname: "Smith"
}

function func(a, b) { 
  alert( this[a] + ' ' + this[b] )
}

*!*
func.call(john, 'firstName', 'surname')  // "John Smith"
*/!*

Now func is called with this = john and given arguments, so it outputs john['firstName'] and john['surname'].

The func.call(context, args...) is essentially same as the simple call func(args...), but additionally sets this.

apply

The func.apply is same as func.call, but it accepts an array of arguments instead of a list.

The following two lines are same:

func.call(john, 'firstName', 'surname') 

func.apply(john, ['firstName', 'surname'])

In a way, apply is more powerful than call, because it allows to pass an array:

var args = ['firstName', 'surname']
func.apply(john, args)

Summary

As method
obj.func(...)    // this = obj
obj["func"](...)

As function
func(...)        // this = window

In new
new func()       // this = {} (new object)

call/apply
func.call(context, arg1, arg2, ...)  // this = context
func.apply(context, [args])

Tutorial

Donate

Donate to this project