0

I'm trying to create an object called List. This object has a method add which simply pushes a task object onto this tasks array. I also built a load method to load items from a url.

My issue is I can't seem to reference the add method from within the load method, I get the following error:

Uncaught TypeError: Object # has no method 'add'.

How do I reference the add method from within the load method? The code I am using is below.

function List(){
    this.tasks = new Array();
    this.add = function(taskItem){
        this.tasks.push(taskItem);
    };
    this.load = function(url){
        $.getJSON(
            url,
            function(data){
                $.each(data, function(key,val){
                    var task = new Task({
                        id:val.pkTaskId,
                        title:val.fldName,
                        status:val.fldStatus
                    });
                    this.add(task);
                });
            }
            );
    }
}

var userList = new List();
userList.load(url)

4 Answers 4

2

Try this:

function List(){
    this.tasks = [];  // prefer [] over new Array()
    this.add = function(taskItem){
        this.tasks.push(taskItem);
    };
    this.load = function(url){
        var self = this;
        $.getJSON(
            url,
            function (data){
                $.each(data, function(key,val){
                    var task = new Task({
                        id:val.pkTaskId,
                        title:val.fldName,
                        status:val.fldStatus
                    });
                    self.add(task);
                });
            }
        );
    }
}

The issue is that this is not what you think it is in the Ajax callback. The callback function is not called in the object's context, it is called in the global context (so this will point to the window object).

Saving an object reference (by convention called self) beforehand is necessary.


this will not always point to the object instance a function "belongs to". In fact, a function does not belong to an object in the same way it does in other languages. this maintains the context a function is called in. Any function can be called in any context:

function A() {
  this.val = "foo";
  this.say = function () { alert( "A: " + this.val ); };
}

function B() {
  this.val = "bar";
  this.say = function () { alert( "B: " + this.val ); };
}

function test() { alert( "T: " + this.val ); }

var a = new A(), b = new B();

a.say()         // alerts "A: foo"
b.say()         // alerts "B: bar"
b.say.call(a);  // alerts "B: foo";      (.call() switches the context)
test()          // alerts "T: undefined" (val does not exist in window)
test.call(b)    // alerts "T: bar"       (Ah!)

Unless you define context implicitly (b.say() implies that this will be b) or explicitly (by using call() or apply()), the context will be the global context - which in a browser is the window object. And that's exactly the case for your Ajax callback.

Sign up to request clarification or add additional context in comments.

1 Comment

@Lucas You're welcome. (It's a very common misconception. Also see the small addition I made to the explanation code.)
2

The context for jQuery Ajax callbacks is an object that represents the options used to make the Ajax request. That is, the options object passed to the call to $.ajax(options), merged with $.ajaxSettings. You can override the context by setting the context option. This means calling $.ajax() instead of $.getJSON().

$.ajax({
    context: this,
    url: url,
    dataType: 'json',
    success: callback
});

Edit: Not sure why I was downvoted. It works the way I said. Try it here:

const ajaxOpts = {
    url: URL.createObjectURL(new Blob([null], { type: "application/json" })),
    dataType: 'json',
    success(data) {
        console.log(`this.foo(): '${this.foo()}'`);
    },
}

$.ajax(ajaxOpts);

$.ajax({
    ...ajaxOpts,
    context: {
        foo() {
            return "bar";
        },
    },
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

So, plugging this into the code from the question should work:

function List(){
    this.tasks = new Array();
    this.add = function(taskItem){
        this.tasks.push(taskItem);
    };
    this.load = function(url){
        $.ajax({
            context: this,
            url: url,
            dataType: 'json',
            success: function(data){
                $.each(data, function(key,val){
                    var task = new Task({
                        id:val.pkTaskId,
                        title:val.fldName,
                        status:val.fldStatus
                    });
                    this.add(task);
                });
            },
        });
    }
}

var userList = new List();
userList.load(url)

Comments

0

Use this syntax:

function List() {
    this.tasks = new Array();
}

List.prototype.add = function(taskItem) {
    this.tasks.push(taskItem);
}

var list = new List();
list.add(…);

Also, try to improve your accept rate, people will be more willing to help you.

Comments

0

To build off of Tomalak's answer, you could move the declaration of "self" to the main object level. This has proven to be pretty useful in the case of using this within nested object functions.

function List(){
   var self = this;
   self.tasks = new Array();
   self.add = function(taskItem){
      self.tasks.push(taskItem);
   };
   self.load = function(url){
      $.getJSON(
        url,
        function(data){
            $.each(data, function(key,val){
                var task = new Task({
                    id:val.pkTaskId,
                    title:val.fldName,
                    status:val.fldStatus
                });
                self.add(task);
            });
        });
    }
}

var userList = new List();
userList.load(url);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.