Question

I'm experimenting with having an object extended and that object containing multiple functions. I'm having trouble explaining it, so let me just show you my code. Let's start with what is working:

(function ($) {
    $.fn.extend({
        Hello: function() {
            console.log("Well, hello back!");
            console.log($(this));
        }
    });
})(jQuery);

In HTML:

<script>
    $(document).ready(function() {
        $('.target').Hello();
    });
</script>

The console log of $(this) is giving me the callee element "target", as expected.

Now, let's try it this way:

(function ($) {
    $.fn.extend({
        Response: {
            Hello: function() {
                console.log("Well, hello back!");
                console.log($(this));
            }
        }
    });
})(jQuery);

In HTML:

<script>
    $(document).ready(function() {
        $('.target').Response.Hello();
    });
</script>

This is still spitting out "Well, hello back!" in the console, but I'm no longer getting the callee when I use $(this). I'm instead getting the Hello function.

How do I get the callee? I've tried this.parent, which does not work. Please help!

Was it helpful?

Solution

As others have pointed out, you can't really do what you are asking for, but you can do something that looks almost the same.

By capturing 'this' with a closure, you can access it further down in your object structure:

$.fn.extend({
    Response: function () {
        var self = this;
        return {
            Hello: function () {
                console.log(self);
            }
        };
    }
});

$('.target').Response().Hello();

OTHER TIPS

You can't really do that -- it's quite impractical.

There are special rules in JavaScript that dictate what the value of this is when a function is called. One of them is that whenever there is an expression of the form foo.bar() then bar is called and this evaluates to foo for the call.

So when you are run $('.target').Response.Hello() what happens is that this is equal to $.fn.Response, and not the jQuery object that wraps .target.

You could get the desired behavior by overriding the assignment of this by using e.g. Function.prototype.call:

var $target = $('.target');
$target.Response.Hello.call($target);

Of course this is cumbersome, but there's not much choice. The only other solution that would theoretically work I can think of involves the idea of injecting code into jQuery so that $$.Response resolves to something different for each jQuery instance $$. This is probably overkill if the aim of the question is simply to achieve a preferred syntax.

Unfortunately, there is no parent-child relationship between objects in JavaScript. The reason for this is that it is possible for the same object to have multiple parents. This can be difficult to grasp when you are dealing with nested objects (as is the case in the code example you have provided). Consider the following example:

var foo = {
  bar: {
    baz: 1,
    biz: function() { console.log(this) } 
  }
}

foo.bar.biz();
=> { baz: 1, biz: [Function] }

var y = {};
y.foobar = foo.bar

y.foobar.biz();
=> { baz: 1, biz: [Function] }

In both cases, a call to biz will return only the immediately containing object. To attempt to access the parent of the bar object through the this keyword would be ambiguous because there are multiple parents that have been specified.

In your case, your best option would be to use the call function to explicitly specify the function context:

var $target = $('.target');
$.fn.Response.Hello.call($target);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top