53

I've seen How does "this" keyword work within a function?, but I don't see that it answers the following.

Given this code:

var MyDate = function(date) {
    this.date = date;
};

var obj1 = {
    foo: new Date(),
    bar: new MyDate(this.foo)  //  this.foo is undefined
};

var obj2 = {};
obj2.foo = new Date();
obj2.bar = new MyDate(this.foo);  //  this.foo is undefined

var obj3 = {
    foo: new Date(),
    bar: new MyDate(obj3.foo)
};

var obj4 = {};
obj4.foo = new Date();
obj4.bar = new MyDate(obj4.foo);

Why do the first two attempts fail, but the last two work? If this isn't bound to the current object literal, what is it bound to?

Community
  • 1
  • 1
brownegg
  • 693
  • 1
  • 6
  • 6

4 Answers4

116

Javascript is a late binding language. In fact, it is very late binding. Not only is this not bound during compile time, it is not even bound during runtime (as most other late binding languages do). In javascript, this is bound during call time.

The binding rules are quite different from most other OO languages which is why it seems to confuse a lot of people not familiar with javascript.

Basically, how and where you use this in the code does not affect how this behaves (it does not matter if it's a standalone function, an object literal etc.) what determines the value of this is how you call the function.

The rules are:

1 - When a function is called as a constructor, a new object is created and this is bound to that object. For example:

function Foo () {
    this.bar = 1; // when called with the new keyword
                  // this refers to the object just created
}
new Foo().bar;

2 - When called as an object method this refers to the object the method belongs to. Basically the name before the last dot. For example:

foo.bar = 1;
foo.baz = function () {
    alert(this.bar); // this refers to foo when called as foo.baz()
}
foo.baz();

3 - If used outside of any function or if a function is not called as a method this refers to the global object. The javascript spec doesn't give a name to the global object apart from saying that one exists but for browsers it is traditionally called window. For example:

bar = 1;
alert(this.bar); // this refers to the global object
foo = {
    bar: this.bar // also global object
}
function foofoo () {
    alert(this.bar); // also refers to the global object
}
foofoo();

4 - In an event handler (such as onclick etc.) this refers to the DOM element that triggered the event. Or for events not associated with the DOM like setTimeout or XMLHTTPRequest, this refers to the global object. For example:

foo.bar = 1;
foo.baz = function () {
    alert(this.bar); // this would normally be foo but if this
                     // function is assigned to an event it would
                     // point to the element that triggered the event
}
somediv.bar = 2;
somediv.onclick = foo.baz; // clicking on somedive alerts 2 instead of 1

5 - Finally, when a function is called using either the call() or apply() methods this can be reassigned to anything whatsoever (google "mdn function.prototype.call"). In this way, any object in javascript can borrow/steal another objects' methods. For example:

cat = {
    type: "cat",
    explain: function () {
        return "I am a " + this.type;
    }
}
dog = {
    type: "dog"
}
cat.explain.call(dog); // returns "I am a dog"

With Function.bind() in modern javascript implementations we now have another rule:

6 - Functions can also explicitly bind this to an object using the bind() method. The bind method returns a new instance of the function where this is bound to the argument passed to bind. For example:

function explain () {
    return "I am a " + this.type;
}
dog = {
    type: "dog"
}
var dog_explain = explain.bind(dog);
dog_explain(); // returns "I am a dog"

ECMAscript 5 introduced strict mode which changes the meaning of this in functions that isn't called as a method or called with call or apply so we have to add a new rule:

7 - When in strict mode, this isn't allowed to refer to the global object (window in browsers). So when a function is not called as a method or this isn't bound to anything manually via call or apply or bind then this becomes undefined:

"use strict";
function foo () {
    return this;
}
foo(); // returns undefined instead of the global object

ECMAscript 6 introduced arrow functions. Arrow functions change how this behaves by binding early.

8 - In arrow functions, this is bound at the time the function is declared. So this in the following code:

var x = () => {return this};

behaves as if the function is declared like the following code:

var x = function () {return this}.bind(this);

Note that since the this in arrow functions are bound at the time the function is declared you can't use arrow functions if you want to use inheritance. That's because the this in the function will always point to the parent object and will never point to the child object. That means that the only way to make inheritance work with arrow function is to override all arrow functions from the parent object.

slebetman
  • 93,070
  • 18
  • 116
  • 145
  • 2
    Modified answer, forgot the case for when `this` is used outside of functions. – slebetman Nov 18 '12 at 15:47
  • 1
    `this` refers to `window` in event handlers only in IE browsers. In all other browsers, `this` refers to the target of the event. – lanzz Nov 18 '12 at 16:27
  • @lanzz: ah forgot. Actually even on IE it normally refers to the target – slebetman Nov 18 '12 at 23:43
  • In item 5, can you explain where the binding of `this` in `return "I am a " + this.type;` arises from? – Ed Staub Apr 24 '17 at 16:05
  • @EdStaub: From the `.call()` method. The first argument to either `call` or `apply` is what you want `this` to be. See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call – slebetman Apr 24 '17 at 17:26
  • Hi !! Thanks for such a great answer ! I just wonder: `var obj1 = { obj2: { this }}` Will `this` refer to `obj1` or to `obj2` in this case ? – GLAND_PROPRE Jul 02 '17 at 15:05
  • @SébastienGarcia-Roméo: That is a syntax error. – slebetman Jul 02 '17 at 15:36
  • @slebetman thx for answer. It was just pseudo-code for simplicity. Correct line: `var obj1 = { obj2: { fn : function() { var obj_tmp = this; }}}` What `this` refer to in this case? – GLAND_PROPRE Jul 04 '17 at 11:36
  • 1
    @SébastienGarcia-Roméo: Then the answer is it depends. Read the answer above carefully. In this case it does not matter at all how you write your `this` or how you declare your function. All that matters is how you **call** your function. `asyncFunction(obj1.obj2.fn)` and `this` will be the global object or undefined in strict mode. `asyncFunction(function(){obj1.obj2.fn()})` then `this` will be `obj2`. `asyncFunction(obj1.obj2.fn.bind(mango))` then `this` will be `mango` – slebetman Jul 07 '17 at 00:07
14

I think you may be missing a key difference between functions and object literals:

The body of a function is not evaluated until the function is called.

That means the value of this depends on how the function is called. If it's called as a method on an object (e.g. someObj.someFunc()), then this will point to that object within the function body. If it's called as a standalone function (someFunc()). them the code in the body will inherit whatever this is in the caller's environment. But either way, the value of this at the time the function is defined doesn't matter at all.

Whereas an object literal is just an expression; if this appears, and it's not inside a function body contained within the literal, it's just going to be the value of this at the point in the code where that expression appears.

Mark Reed
  • 81,164
  • 14
  • 120
  • 155
9

In Javascript, only function calls establish a new this context. When you call foo.bar(), within the bar function, this will be bound to foo; when you call foo(), inside it this will be bound to window. An object literal constructor is not a method call, so it does not affect this in any way; it will still refer to whatever it was referring to outside of the object literal.

lanzz
  • 38,081
  • 8
  • 81
  • 91
7

this.foo is undefined because in all your examples, this is referring to the global window object. Also, even if you tried obj1.foo, it will still return undefined because the property hasn't been created until the entire expression is evaluated. Try this instead:

var obj1 = {
    foo: new Date(),
    bar: function() {
        return new MyDate( this.foo ); // will work
    }
};

It works because by the time you call obj1.bar(), the object will have been created by then; and because you're in a function, the this object will reference the current object.

0x499602D2
  • 87,005
  • 36
  • 149
  • 233