9

I am asking this from a language design point of view. So I am trying to find out

  1. What is the rationale for the behavior of this?
  2. To what degree the behavior of this was a mistake, or could be improved upon?

To clarify why I'm uneasy about this, consider this example:

var a = {};
a.f = function(){ return this; }
var f = a.f;
// f() != a.f()

Note how easily the object to which f() belong is lost: separated from a, this becomes the global object (window for browsers).

Now consider:

var newA = function(){
    var self = {};
    self.f = function(){ return self; }
    return self;
}

var a = newA();
var f = a.f;
// f() == a.f() !

Without using this at all, we are able to establish and maintain the object context regardless of where or how the method is used. I can't help but think that, with the power that closures provide, this becomes superfluous, and perhaps even a little dangerous...

I'm not on some vendetta against this, or looking to start an argument; I'm merely trying to better understand it. I do appreciate that 'this' can be useful, but recognize that it can be confusing as well... Certainly confusing to beginners, and perhaps to experts as well in sufficiently obscure cases.

And yet, it remains a heavily-used and seemingly well-respected part of the language, in a time when other core aspects of the language seem fair game for shunning (i.e., Crockford and with or new). What am I missing then, that makes this indispensable?

Graeme Perrow
  • 56,086
  • 21
  • 82
  • 121
mike g
  • 1,791
  • 1
  • 18
  • 25
  • What gives you the impression that "this" is a mistake? – Toon Krijthe Feb 12 '09 at 13:26
  • @Gamecat: You have never tried to design a language similar to javascript, do you? – artificialidiot Feb 12 '09 at 13:49
  • I still think this is argumentative. The language implies a problem, cf "How amazing is 'this' (if it is amazing)" – annakata Feb 12 '09 at 16:39
  • This is not a question about js itself. Many people take it as a challenge of how things can be done in js to remedy shortcomings of "this" or a whining about how badly js is designed. I advise people not to answer unless they thought about it from a language design point of view. – artificialidiot Feb 12 '09 at 17:17
  • I had a go at making it seem less argumentative. I don't think that was the author's intention, but looking for a critical appraisal of a language feature is always a bit of a minefield. @mike g: feel free to roll back if you don't like my changes. – Shog9 Feb 12 '09 at 17:18
  • no, the changes are fine ... the discussion was the primary purpose. NPOV is the price we have to pay for a sensible public discussion. – mike g Feb 12 '09 at 21:30

7 Answers7

11

You seem to be expecting this to behave as it does in certain OO languages, where it always refers to the object a method belongs to.

But in JavaScript, a function can be attached to multiple objects, or no object at all. In your example, you've written a function intended to be used in the context of one specific object... But nothing prevents me from taking that function and attaching it to any other object. That's just the nature of the language - functions are first-class, object membership is optional.

Therefore, this refers to the context in which a function is called. Right now, that's either an arbitrary object (specified via ., .apply, or .call()) or the global object. In future versions of the language, it will refer to the context in which the function was defined: the global object for global functions, the outer this for inner functions; you can view this as a correction of a design flaw, as in practice being able to refer to the global object using this was not particularly useful.

Shog9
  • 156,901
  • 35
  • 231
  • 235
8

I don't think making "this" unbound was a mistake. It can sometimes be confusing at first, but there are good reasons for the way it is. The first one that comes to mind is that, since JavaScript is not a class based language, functions are not associated with any specific class, so there's not a consistent way to automatically bind "this" to the correct object instance. For example,

function Person(first, last, age) {
    this.firstName = first;
    this.lastName = last;
    this.age = age;
}

Person.prototype.getFullName = function() {
    return this.firstName + " " + this.lastName;
};

"this" needs to refer to a Person object, but the function assigned to Person.prototype.getName doesn't have any way of knowing how it's going to be used, so "this" needs to be bound to whatever object it is called on.

Where this causes a problem, is when you have nested functions.

// This is a really contrived example, but I can't think of anything better
Person.prototype.getInfo = function() {
    // get name as "Last, First"
    function getNameLastFirst() {
        // oops. "this" is the global object, *not* the Person
        return this.lastName + ", " + this.firstName;
    }

    // expect something like "Crumley, Matthew: Age 25",
    // but you get "undefined, undefined: Age 25"
    return getNameLastFirst() + ": Age " + this.age;
};

The syntax artificialidiot suggested would be convenient, but it's pretty easy to bind "this" to a specific object using apply:

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

Person.prototype.getInfo = function() {
    // get name as "Last, First"
    var getNameLastFirst = bind(function () {
        return this.lastName + ", " + this.firstName;
    }, this);

    return getNameLastFirst() + ": Age " + this.age;
};

or the more "traditional" method using closures:

Person.prototype.getInfo = function() {
    var self = this;

    // get name as "Last, First"
    function getNameLastFirst() {
        return self.lastName + ", " + self.firstName;
    }

    return getNameLastFirst() + ": Age " + this.age;
};
Community
  • 1
  • 1
Matthew Crumley
  • 101,441
  • 24
  • 103
  • 129
  • Matthew Crumley, (I don't know why the "@" sign is not working but hopefully you will see this post). I am trying to follow your code in your answer, but maybe I'm missing something here? Please tell me, where are you defining the function `getNameFirstLast`? Is this a typo and why it's return values are undefined? Thanks. – Chris22 May 09 '14 at 20:53
  • @Chris22 Oops, that was a typo. it should be `getNameLastFirst()`. The reason it returns `undefined` in the first example is because it's called without a context, so `this` is bound to the global scope by default. So (assuming there are no global variables named `lastName` or `firstName`) both properties would be undefined instead of getting the values from the `Person` object like you might expect. – Matthew Crumley May 12 '14 at 13:12
  • +1 Matthew Crumley Ok, thanks. I understand the scope of 'this'. The typo was just distracting :-) – Chris22 May 12 '14 at 19:02
  • @Chris22 That's understandable. Sorry about that. – Matthew Crumley May 13 '14 at 17:25
3
  • It wasn't
  • Lots of OO stuff
  • It's good the way it is
Svante Svenson
  • 12,315
  • 4
  • 41
  • 45
3

I think unbound "this" is a mistake. Otherwise it is quite handy. Unbound "this" opens the possibility of misinterpreting the context most prominently apparent in event handling of browsers. Also javascript libraries have different opinions of what "this" should refer to in event handling and many callback constructs (like map, filter).

Removing unbound "this" probably wouldn't make things any more difficult.

Edit: I guess an alternative syntax example will make my stance clearer.

function Foo()
{
    //both this refer to the Foo instance
    this.blah=this.function(){this.bar;};

    //second this refers to baz
    this.blah=baz.function(){this.bar;};

    //second this refers to anonymous function itself
    this.blah=function(){this.bar;};
}
artificialidiot
  • 5,309
  • 29
  • 27
1
  • It should be called 'self' instead
  • Anything that refers to current objects state.
  • By choosing 'self' as the name for 'this', and passing it explicitly (as the first argument) to all methods. This way you can easy tell instance method from static method or from function.

Sorry, but I really like Python ;-)

Abgan
  • 3,696
  • 22
  • 31
1

I think the unbound 'this' keyword is necessary because JavaScript is a prototype-based language. Someone better informed can probably fill in the details here.

The fact that it is, is mightily unhelpful though. Especially if you want to pass the method of an object to a higher-order function, things start to get ugly (following examples with a little help from MooTools):

myArray.each(myObject.foo);

Will not work, because the 'this' in myObject.foo will refer to myArray instead of myObject. Instead:

myArray.each(myObject.foo.bind(myObject))

Which seems very ugly to me. That's why I usually don't program in an object-oriented way in JavaScript, but I rely heavily on closures instead.

rix0rrr
  • 9,856
  • 5
  • 45
  • 48
1

Consider the idiom a.f() as a shorthand for:

a.f.call(a);

It's by definition a call to the function f, using scope a.

var f = a.f;
f(); // f.call(this);
a.f(); // f.call(a);

If this and a are not the same object, f() and a.f() will use different scopes and therefore may behave differently. Consider the distinction between static and class methods in other languages:

class Foo {
public:
    static void a(Foo *scope) {
        // do something with given scope
    };

    void b() {
        a(this); // do something with the scope of this object
    };
};

Foo foo;
Foo bar;

foo.a(&bar) != foo.b(); // just like f() != a.f()
foo.a(&foo) == foo.b(); // just like f.call(a) == a.f()
Ates Goral
  • 137,716
  • 26
  • 137
  • 190