scusyxx
scusyxx

Reputation: 1244

Javascript 'this'

Can you explain me why does the second call of fn gives an error? The code is below.

function Test(n) {
  this.test = n;

  var bob = function (n) {
      this.test = n;
  };

  this.fn = function (n) {
    bob(n);
    console.log(this.test);
  };
}

var test = new Test(5);

test.fn(1); // returns 5
test.fn(2); // returns TypeError: 'undefined' is not a function

Here's a JSfiddle that reproduces the error http://jsfiddle.net/KjkQ2/

Upvotes: 6

Views: 306

Answers (3)

jAndy
jAndy

Reputation: 236012

When calling bob(n) within .fn, it is called within the global context (window in a browser). Now, you're setting window.test = n; which basically overwrites your function test object you created earlier.

If we you write this more explicit, it becomes more obvious:

// in the global scope, `test` gets written to the `global object`
// window.test = new Test(5);
var test = new Test(5);

test.fn(1); // returns 5
test.fn(2); // returns TypeError: 'undefined' is not a function

You can "workaround" this issue by calling bob() with an explicit context, using .call() for instance:

this.fn = function (n) {
   bob.call(this,n);
   console.log(this.test);
};

The root of evil here is, that the value of this is dynamically assigned during run-time. Don't get me wrong, its actually a great feature of ECMAscript - it's just the problem for you here. When you call a function, "just like that", this will always reference the global object.

Upvotes: 3

Ruan Mendes
Ruan Mendes

Reputation: 92274

Your bob function is called from the global scope. Thefore, this.test is pointing at a global variable named test which is overwriting the variable you created. If you run console.log(window.test), you'll what's happening.

For your code to behave as intended, you would need one of the following

function Test(n) {
  this.test = n;

  // If a function needs 'this' it should be attached to 'this'       
  this.bob = function (n) {
      this.test = n;
  };

  this.fn = function (n) {
    // and called with this.functionName
    this.bob(n);
    console.log(this.test);
  };
}

OR

function Test(n) {
  this.test = n;

  var bob = function (n) {
      this.test = n;
  };

  this.fn = function (n) {
    // Make sure you call bob with the right 'this'
    bob.call(this, n);
    console.log(this.test);
  };
}

OR closure based objects

// Just use closures instead of relying on this
function Test(n) {
  var test = n;

  var bob = function (n) {
      test = n;
  };

  this.fn = function (n) {
    bob(n);
    console.log(test);
  };
}

Upvotes: 6

gen_Eric
gen_Eric

Reputation: 227240

You want to call bob.call(this, n), not just bob(n).

When you call bob(n), the value of this is not your object, it's window. Therefore, your test variable is replaced with 1.

In jsFiddle, the code is wrapped in a function, so window.test does not exist at first.

Upvotes: 0

Related Questions