2

I have an object with a number of functions:

var someScopeScopedObject = {
  myFunction1: function () {
    console.log('foo');
  },

  myFunction2: function () {
    this.myFunction1();
  }

 init: function (callback) {
    callback();
  }
}

So if I call someScopeScopedObject.myFunction2 that works fine

But If I do someScopeScopedObject.init(someScopeScopedObject.myFunction2) then I get an error that this.myFunction1 is undefined

Why is this not valid when I call into the function that way?

rogy
  • 440
  • 6
  • 23
  • 1
    possible duplicate of [How does "this" keyword work within a JavaScript object literal?](http://stackoverflow.com/questions/133973/how-does-this-keyword-work-within-a-javascript-object-literal) – Andreas Jan 07 '15 at 11:43
  • Its really not a duplicate – Naeem Shaikh Jan 07 '15 at 11:45
  • When you pass `myFunction2` as an argument to something, it loses the reference to the object it belongs to. `this` now refers to the function itself, and not `someScopeScopedObject`, hence your problem :) – Niels Abildgaard Jan 07 '15 at 11:51
  • This is also why some libraries take an object context for methods, ie. jasmine's `spyOn(someScopeScopedObject, "myFunction2")`, to keep the scoping correct. – Niels Abildgaard Jan 07 '15 at 11:52
  • @NielsAbildgaard thank you! thats the answer I was looking for, is there any good way around this? – rogy Jan 07 '15 at 11:54
  • @rogy Well I guess I should make it an answer then. You can do the same as jasmine... I'll post an example. – Niels Abildgaard Jan 07 '15 at 11:54

3 Answers3

3

The keyword this refer's to the functions scope. Not the global scope. You declare a function called myFunction2, the this refers to the scope of this function and not the scope in which this function is declared.

It isn't like in languages, like C#, where this refers to the current instance of the class, whether you are in one another method.

Christos
  • 53,228
  • 8
  • 76
  • 108
1

this refers to the function in which it is declared, So in your example,

var someScopeScopedObject = {
  myFunction1: function () {
    console.log('foo');
  },

  myFunction2: function () {
    this.myFunction1();
  }

 init: function (callback) {
    callback();
  }
}

myFunction1 is not declared inside myFunction2, so

` myFunction2: function () {
        this.myFunction1();
      }.

is not possible, because this.myFunction1(); tries to call method myfunction1 which should be declared inside myfunction1 which is not declared there.

Edit:

  1. someScopeScopedObject.myFunction2(); is possible because you are calling the method myFunction1 which is defined inside someScopeScopedObject

  2. someScopeScopedObject.init(someScopeScopedObject.myFunction2); is not possible because, you pass someScopeScopedObject.myFunction2 as a callback function in this case here to the init function,

init: function (callback) { callback(); }

It then calls

myFunction2: function () { this.myFunction1(); }

here you refer this.myFunction1();--- This doen not exist, since you are refering to myfunction1 defined inside myfunction2, which is not!

Naeem Shaikh
  • 15,331
  • 6
  • 50
  • 88
  • So why does it work when calling it directly? but not through the `init` – rogy Jan 07 '15 at 11:42
  • `this` refers to the function in which it is used, if you are using `this.myfunction1` inside `myfunction2`, then you are trying to call methid `myfunction1` defined inside `myfunction2` – Naeem Shaikh Jan 07 '15 at 11:43
  • http://jsfiddle.net/fajdoxgo/ im still not understanding why the first one works, and the second one doesn't – rogy Jan 07 '15 at 11:51
  • See in first one you are calling the` myfunction2` which is defined in the object `someScopeScopedObject `, and in your 2nd one you are not calling the same method, **because**, your `this.myFunction1()`, does not exist. because `myfunction1` is not defined inside `myfunction2` instead it is defined in `someScopeScopedObject` – Naeem Shaikh Jan 07 '15 at 11:56
0

When you pass myFunction2 as an argument to something, it loses the reference to the object it belongs to. this now refers to the function itself, and not someScopeScopedObject, hence your problem :)

This is also why some libraries take an object context for methods, ie. jasmine's spyOn(someScopeScopedObject, "myFunction2"), to keep the scoping correct.

An example work-around (like the one used by jasmine) could look like this:

var someScopeScopedObject = {
  myFunction1: function () {
    console.log('foo');
  },

  myFunction2: function () {
    this.myFunction1();
  },

  init: function (context, callbackName) {
    context[callbackName]();
  }
}

This would allow you to do the following:

someScopeScopedObject.init(someScopeScopedObject, "myFunction2");

This is not the prettiest. Alternatively, you can bind the object to the function (so the scope stays the same). This assumes your original code in someScopeScopedObject:

someScopeScopedObject.init(someScopeScopedObject.myFunction2.bind(someScopeScopedObject));

Neither of these are pretty, and, really, that's because it is weird to externally feed a function belonging to an object as a callback to that object.

The best solution might be:

someScopeScopedObject.init(function() {
    someScopeScopedObject.myFunction2();
});
Niels Abildgaard
  • 2,662
  • 3
  • 24
  • 32