0

Could you tell me why this will not work? (note the this keyword)

var post = {
    url: 'http://myurl.com',
    add: function() {
        window.location = this.url + '/add';
    },
    edit: function() {
        window.location = this.url + '/edit';
    }
};

Somewhere else in the code:

post.url = '<?php echo BASE_ADMIN . $postType ?>';

$(document).ready(function() {

    $("#listing").aJqueryPlugin({
    ...
    // Buttons and their callbacks
    buttons : [
        {name: 'Add', bclass: 'add', onpress : post.add},
        {name: 'Edit', bclass: 'edit', onpress : post.edit},
    ],
   ...
});

The line

post.url = ....

behaves as expected. The url property in post is updated.

However, when I click on the Add or Edit buttons, and I enter their functions, this.url is undefined because this references the button instead of the post object. Why? What should I do then to reference the url property from a callback?

Luis Martin
  • 910
  • 3
  • 14
  • 31
  • 2
    See "closures" or `Function.bind`. –  Nov 29 '12 at 20:13
  • This is not related to closures, but execution context. – Christoph Nov 29 '12 at 20:15
  • possible duplicate of [Javascript: Object Literal reference in own key's function instead of 'this'](http://stackoverflow.com/questions/10711064/javascript-object-literal-reference-in-own-keys-function-instead-of-this) – Bergi Nov 29 '12 at 20:23

1 Answers1

2

Since you're using jQuery, you can use $.proxy.

    {name: 'Add', bclass: 'add', onpress : $.proxy(post, "add")},
    {name: 'Edit', bclass: 'edit', onpress : $.proxy(post, "edit")},

This returns a new function that will call the method of the object named by the string.


It's effectively doing this:

{name: 'Add', bclass: 'add', onpress : function() {
                                           return post["add"].apply(post, arguments);
                                       },
{name: 'Edit', bclass: 'edit', onpress : function() {
                                           return post["edit"].apply(post, arguments);
                                         },

Because the value of this in a function is dependent on how the function was called, you sometimes need alternate means to ensure that you get the correct value.


You could also set these in your original object, as long as you know you always want this to refer to that object.

var post = {
    url: 'http://myurl.com'
};
post.add = $.proxy(function() {
    window.location = this.url + '/add';
}, post);
post.edit = $.proxy(function() {
    window.location = this.url + '/edit';
}, post);

This uses the other signature of $.proxy, which lets you pass the function directly followed by the desired this value.

I Hate Lazy
  • 47,415
  • 13
  • 86
  • 77
  • Thanks for you reply! This works after a little adjustment, because **this** inside the "add" and "edit" functions now references **Window**. So I had to change this.url + "/add" for post.url + "/add". – Luis Martin Nov 29 '12 at 20:25
  • @LuisMartin: It references `window` after using `$.proxy`? It shouldn't. – I Hate Lazy Nov 29 '12 at 20:27
  • I can't grasp why this happens in callbacks. Referencing **this** externally works however: this.url = 'whatever'; – Luis Martin Nov 29 '12 at 20:29
  • @LuisMartin: The value of `this` depends on how the function was called. If you do `post.add()`, the value of `this` in `add` will be automatically set to `post`. But if you *pass* `post.add` to another function, you're only passing the method, not the object, so it forgets where it came from. Using `$.proxy` returns a new function that uses the `post` object to invoke the `"add"` method so that its `this` value is retained. – I Hate Lazy Nov 29 '12 at 20:31
  • @user1689607: It really references **Window** inside the function. In fact changing **this** for **post** works. – Luis Martin Nov 29 '12 at 20:36
  • @LuisMartin: Yes, changing `this` to `post` will clearly work because you're referencing the `post` object explicitly instead of hoping `this` will reference it. But if you did `onpress: $.proxy(post, "add")`, then `this` in the `add` method will refer to the `post` object. If it doesn't, then you did something wrong. – I Hate Lazy Nov 29 '12 at 20:41
  • yes I did it wrong, sorry. I was doing $.proxy(post.add) instead of $.proxy(post, "add") Now it works. I appreciate a lot your help! – Luis Martin Nov 29 '12 at 23:41