5

Warning: Buggy JavaScript code first!

// 1: buggy counter
// -----------------
// problem: 'this' can be "broken"
var Counter1 = function() {
    this.count = 0;
    this.increment = function() {
        function increment_helper(){
            ++this.count; // this refers to window (global), not to the object that Counter1 was called with
        }
        increment_helper();
    }
    this.reset = function() { this.count = 0; }
    this.get_count = function() { return this.count; }
}

var counter1 = new Counter1();
counter1.increment();
document.writeln("<p> 1: " + counter1.get_count() + "</p>");

As it can be seen, the this in increment_helper rather refers to the global scope (or to window scope to be precise IF I've got it right) instead of referring to the this of the enclosing closure. So that, the output will be:

0

instead of

1

I'll list the better (still not perfect, but okay) solution and then will ask the main question. So, the solution (IMHO) can be sorted out like this:

// 2: better counter
// -----------------
// solved: 'that' can't be "broken"
var Counter2 = function() {
    var that = this;
    that.count = 0;
    that.increment = function() {
        function increment_helper(){
            ++that.count; // that refers right to the object the Counter1 was called with
        }
        increment_helper();
    }
    that.reset = function() { that.count = 0; }
    that.get_count = function() { return that.count; }
}

var counter2 = new Counter2();
counter2.increment();
document.writeln("<p> 2: " + counter2.get_count() + "</p>");

So, the main question is: why this is special like noone else in JavaScript? Why it's only this and for what purpose?

Thanks a lot!

Update: Following @Jordan's comment below (where he explained the difference between relative variables like this and scoped variables like regular ones), rephrasing my questions:

A: what is the general rationale behind relative variables?

B: why this is the only relative variable in JavaScript? Any rationale on why the relative/scoped concept can be (exclusively) applied to every variable?

BreakPhreak
  • 10,940
  • 26
  • 72
  • 108
  • A good time to note that standard practice is using another variable [such as self](http://www.javascriptkata.com/2007/05/14/how-to-use-the-self-with-object-oriented-javascript-and-closures/) to refer to your object when working with nested structures. – Brad Christie Jun 14 '11 at 15:39
  • Please see: http://stackoverflow.com/questions/133973/how-does-this-keyword-work-within-a-javascript-object-literal/134149#134149 – Jordan Jun 14 '11 at 15:40
  • @Jordan: good reference, thanks! I know how this is defined. The question is why it is defined in that way any why that way is applied only to `this` and to no one else? – BreakPhreak Jun 14 '11 at 15:42
  • `this` is a relative variable, and has different meanings depending on the context, while any other variables (anyone else) is a scoped variable that has meaning depending on where it's defined. Is your question about the theory behind how `this` is scoped in Javascript in general? – Jordan Jun 14 '11 at 15:46
  • @Jordan: yes! Thanks for introducing me to relative and scoped variables concept. I am asking two questions (just rephrasing): [A] what is the rationale behind relative variables? and [B] why `this` is the only relative variable in JavaScript? Any rationale on why the relative/scoped concept can be (exclusively) applied to every variable? Hopefully succeeded to rephrase the question in a better way :) – BreakPhreak Jun 14 '11 at 15:52

2 Answers2

2

A. In JavaScript, unlike in other C-like OO languages, functions are first class objects. You can copy functions around, pass them into other functions, return them from functions, create new ones. Functions are not like methods in Java or C# that are permanently affixed to the class declaration. This means that this has to be relative to the function itself and how it was called. It doesn't make sense otherwise.

So as a silly example:

var o = {
  name: "My name is o",
  foo: function () { alert(this.name); }
};

var p = {
  name: "My name is p",
};

p.foo = o.foo;
p.foo(); // "My name is p"

That can't be done in other OO C-type languages, but it can in JavaScript. Hence this has to be relative. Yes, it causes problems, but the language becomes incredibly expressive with it.

B. arguments is also relative.

jmbucknall
  • 2,061
  • 13
  • 14
  • so, in fact, you say that since JS is a "dynamic prototype-based language" then methods can be bound to *any* object, hence `this` must conform the execution context rather then follow nested scoping traditional rule? – BreakPhreak Jun 14 '11 at 22:17
  • @BreakPhreak There are four different ways of calling a function and they all define what happens to `this`. Method invocation: as above, `this` is the object. Function invocation: calling a function "bare", say like alert(), `this` is the global object. Constructor invocation: called with `new` and `this` is the new object. Apply invocation: called with `apply` or `call`, you define what `this` is. – jmbucknall Jun 15 '11 at 01:03
  • Thanks for listing the rules (I know them). The question in my comment was rather conceptual: can it be said that relative variables allow methods/functions to be bound to *any* object, defined at the moment of calling the method? Huh, I guess that the answer would be "yes", it's just that I'll need for some time to understand the issue completely. – BreakPhreak Jun 15 '11 at 08:42
1

JavaScript has only superficial resemblance to Java. When you look past the syntax you notice the object model is totally different (no classes) and it is more of a functional language than an object-oriented one. Netscape wanted JavaScript to look like Java for marketing reasons. To that end it picked up several features you might not otherwise expect, such as bitwise operators (in a scripting language with no integer data type.) this is one of those features.

Historically, the reason this is defined the way it is, is because that's as close as the designers could get to mimicking the meaning it has in Java.

Also, consider what happens when you write the nearest equivalent code in Java:

class Foo {
    int count = 0;
    IncrementHelper helper = new IncrementHelper() {
        public void increment() {
            ++this.count; // doesn't compile
        }
    }

    public void increment() { helper.increment(); }
    public void reset() { this.count = 0; }
    public int getCount() { return this.count; }
}

The indicated line doesn't even compile (you have to say ++Foo.this.count) because within the inner class, this isn't an instance of Foo.

gatkin
  • 1,902
  • 12
  • 12