0
function ObjectProvider() {
    this.url = "ajax/inbounds.php"
    this.inBounds = function () {
        this.removeMarkers();
        var url_string = this.url;
        $.getJSON(url_string, function (data) {
            for (i = 0; i != data.length; ++i) {
                this.createObject(data[i]);
            }
        });
    };
    this.createObject = function (_data) {};
    this.removeMarkers = function () {};
};

So the line

this.createObject( data[i] );

is having some issues, but

this.removeMarkers();

works fine.

Both functions are defined in the ObjectProvider object. Have tried adding a function called test() and just doesn't like anything being called within the JSON callback function.

Envil
  • 2,687
  • 1
  • 30
  • 42
danski
  • 353
  • 3
  • 12
  • How is my question flagged as duplicate when it was asked before the one it's suggested as a duplicate of ? June 4, 2012 vs November 29, 2013... – danski Oct 07 '17 at 05:41

4 Answers4

5

This is a typical scoping problem; the this inside your $.getJSON() callback function is no longer the same this.

To resolve the problem, you have to keep a reference to this before calling $.getJSON(), e.g.

var self = this;
$.getJSON(url_string, function(data) {
    // self.createObject( data[i] );
});

Or, bind the success callback function using $.proxy:

$.getJSON(url_string, $.proxy(function(data) {
    this.createObject(data[i]);
}, this));
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
  • var test_function = this.test(); cheers mate. I came back to see if that's what it was. I tried that and it seemed to work. – danski Jun 04 '12 at 08:03
  • @danski hmm, that's storing the output of `this.test()` in a variable; that wouldn't work in your case though, as you need to call `.createObject()` multiple times inside the callback. – Ja͢ck Jun 04 '12 at 08:08
  • I've gone with the self = this option. Is working well now. I was thinking that I was creating a reference to the this.test() function with my first attempted solution. self = this is better because I'm accessing a few functions and attributes anyway. Cheers. – danski Jun 04 '12 at 08:16
1

The this in the ajax callback is in different context.

Solution: cache it in a local variable.

this.inBounds = function(){
    var self = this;
    self.removeMarkers();
    var url_string = this.url;
    $.getJSON(url_string, function(data) {
        for( i=0; i != data.length; ++i ){
            self.createObject( data[i] );
        }
    });
};
xdazz
  • 158,678
  • 38
  • 247
  • 274
0

The thing is, you're passing a function to getJSON and that function will be called with "this" pointing to either the global object or to some custom object defined by jQuery. If you want "this" inside the callback to point to your object, use bind method (which is not available in some older browsers, but you can easily find projects that emulate this method).

$.getJSON(url_string, function(data) {
    for( i=0; i != data.length; ++i ){
        this.createObject( data[i] );
    }
}.bind(this));

or define a helper variable that will point to "this" outside this callback and use the variable inside.

var self = this;

$.getJSON(url_string, function(data) {
    for( i=0; i != data.length; ++i ){
        self.createObject( data[i] );
    }
});
Rafael
  • 18,349
  • 5
  • 58
  • 67
0

I don't like getJSON for this very reason, it's too limited compare to ajax.

Basically, when the success function is executed, this is not pointing anymore to your ObjectProvider instance. To solve that, you can use ajax instead, that provide an useful context property:

function ObjectProvider(){
  this.url = "ajax/inbounds.php"
  this.inBounds = function(){
    this.removeMarkers();

    var url_string = this.url;
    $.ajax({
      url: this.url,
      dataType: "json",
      context: this,
      success: function(data) {
        for( i=0; i != data.length; ++i ){
            this.createObject( data[i] );
        }
    });
  };

  this.createObject = function(_data){};
  this.removeMarkers = function(){};
};

Otherwise you could use bind:

$.getJSON(url_string, function(data) {
    for( i=0; i != data.length; ++i ){
        this.createObject( data[i] );
    }
}.bind(this));

In order to create a new function "bound" to a specific context object, but only in the browsers that are supporting ES5 or have a shim for that (as the link above describe).

There is also the version mentioned, the closure to store the instance, but I personally prefer avoid that when is not strictly necessary. Nowadays, for this kind of behavior, it's considered an old practice.

And talking about closure: you don't need to declare the function inside the constructor. In that way they're added dynamically all the times, and if they're not accessing to some inner variables there is no reason, it's just a waste of resource. To be clear, in your case, if you have:

var a = new ObjectProvider();
var b = new ObjectProvider();

console.log(a.createObject === b.createObject) // false

A better approach could be:

function ObjectProvider() {
}

ObjectProvider.prototype = {
    constructor: ObjectProvider,

    url: "ajax/inbounds.php",

    inBounds: function () { /* ... */ },
    createObject: function () { /* ... */ },
    removeMarkers: function () { /* ... */ }         
}

var a = new ObjectProvider();
var b = new ObjectProvider();

console.log(a.createObject === b.createObject) // true

But at that point, if you don't have any dynamic property (e.g. the URL is always the same, is not passed to the constructor as parameter) maybe you don't have the need to have different instances as well, and you can just and up with:

var ObjectProvider = {
    url: "ajax/inbounds.php",

    inBounds: function () { /* ... */ },
    createObject: function () { /* ... */ },
    removeMarkers: function () { /* ... */ }         
};

And use it as "singleton":

ObjectProvider.createObject()
ZER0
  • 24,846
  • 5
  • 51
  • 54