Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why 'this' resolution is so special in JavaScript?

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?

like image 819
BreakPhreak Avatar asked Jun 14 '11 15:06

BreakPhreak


3 Answers

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.

like image 189
jmbucknall Avatar answered Sep 28 '22 03:09

jmbucknall


This article will give you deep understanding on how "this" works in javascript.

like image 24
Andreas Köberle Avatar answered Sep 28 '22 04:09

Andreas Köberle


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.

like image 34
gatkin Avatar answered Sep 28 '22 03:09

gatkin