Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript 'this'

Tags:

javascript

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/

like image 725
scusyxx Avatar asked Oct 17 '12 20:10

scusyxx


2 Answers

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);
  };
}
like image 152
Juan Mendes Avatar answered Oct 21 '22 21:10

Juan Mendes


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.

like image 43
jAndy Avatar answered Oct 21 '22 20:10

jAndy