1

The click function in jQuery is defined as .click( handler ), so why can't I do the following:

var object1 = $("#object1");
var object2 = $("#object2");
object1.click(object2.toggle);

The following, however, works:

var object1 = $("#object1");
var object2 = $("#object2");
object1.click(function() { object2.toggle(); });

The question is, why do I have to encapsulate the function handler in another function in order for it to work? I know that in the first example, the toggle function would be receiving all the events data, but it doesn't seem to affect (also, same happens with other functions such as show, hide, fadeIn, etc)

UPDATE:

Regarding the arguments, this also seems to work:

var object1 = $("#object1");
var object2 = $("#object2");
object1.click(function(ev) { object2.toggle(ev); });

So, the invalid arguments don't seem to be a problem.

ANSWER:

The explanation of the problem is what @SeanWessell said, here's a JSFiddle showing the issue in a way I understood it better. It has nothing to do with jQuery though: https://jsfiddle.net/diegojancic/nffcnu8t/

Diego Jancic
  • 7,280
  • 7
  • 52
  • 80
  • Well the `click()` method takes a callback, You need a function in order to call it. there might be a way to do closure in ECMA script 6 but i'm not sure. – Nicolas Feb 14 '17 at 16:25
  • `toggle` is a function too and should be a valid callback as I see it. – Diego Jancic Feb 14 '17 at 16:29
  • @charlietfl the click function receives only one argument, if I do `function(a,b,c) {}`, then `b` and `c` are `undefined`. – Diego Jancic Feb 14 '17 at 16:39
  • In theorie it should, but since every jquery function return the jquery element, i'm not sure it is count as a callback. – Nicolas Feb 14 '17 at 16:42
  • it could work if `toggle` returned the actual `toggle` function – Nicolas Feb 14 '17 at 16:42
  • @Nicolas makes no sense. I'm not calling `toggle()` I'm just passing the reference. The return parameter makes no difference as it's not executed until I click on `object1`. See https://jsfiddle.net/pngeLyf8/1/ – Diego Jancic Feb 14 '17 at 16:48
  • Even if you can technically get this to work, you definitely shouldn't write your code without respecting the calling API. A type checker like TypeScript or Flow would definitely complain (assuming you had the definition files for jQuery). – Kevin Ji Feb 14 '17 at 17:49
  • Still if you `console.log(object2.toggle(e))` You will still get the Jquery element, not the function, that's why – Nicolas Feb 14 '17 at 17:55

2 Answers2

1

This will not work because it will assign the handler to the click event as

function ( speed, easing, callback ) {
        return speed == null || typeof speed === "boolean" ?
            cssFn.apply( this, arguments ) :
            this.animate( genFx( name, true ), speed, easing, callback );
    }

When you try assigning the handler like object1.click(object2.toggle); it is the same as doing this.

object1.click(
  function(speed, easing, callback) {
    return speed == null || typeof speed === "boolean" ?
      cssFn.apply(this, arguments) :
      console.log(this)
    this.animate(genFx(name, true), speed, easing, callback);
  }
);

The handler doesn't care about the reference to object2 in your example. It will take the toggle function as defined by jquery and assign that as the handler however it is no longer chained.

Assigning the handler like object1.click(object2.toggle); takes the literally function from object2.toggle and applies that as the handler. It would be the same as applying a property of one object to another.

var obj1 =  {
"prop1":"obj1PropValue"
};

var obj2 = {
"prop1":"obj2PropValue"
}

obj1.prop1 = obj2.prop1;

Even though obj2.prop1 was assigned from obj1 javascript doesn't care which object the property came from it only cares about the value that was assigned. Same idea applies for functions in javascript.

A function is nothing more than a property VALUE. Value being the keyword.

JavaScript is designed on a simple object-based paradigm. An object is a collection of properties, and a property is an association between a name (or key) and a value. A property's value can be a function, in which case the property is known as a method. In addition to objects that are predefined in the browser, you can define your own objects. This chapter describes how to use objects, properties, functions, and methods, and how to create your own objects.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects

Sean Wessell
  • 3,490
  • 1
  • 12
  • 20
  • Not sure I understand. Can you please explain? I see in the code you posted that the callback is being passed directly to the `animate` function. – Diego Jancic Feb 14 '17 at 16:42
  • You are assigning the handler as the function i posted above. It is no longer a chained event attached to jQuery. Its the same as pulling the function out like this fiddle. https://jsfiddle.net/SeanWessell/997wq7te/ therefor there is no more reference to 'this' where this is the chained jQuery object. Since the function is no longer chained to the jQuery object genFX is no longer in scope. I threw a console.log of this into the function and it is now the clicked element instead of being chained to object2. – Sean Wessell Feb 14 '17 at 16:49
  • Your example is obviously not going to work. You do `object1.click(toggle);` but `toggle` has no reference to any object. Doing `object1.click(function() { toggle(); });` in your example doesn't work either. – Diego Jancic Feb 14 '17 at 17:06
  • Correct. That is my point. There is no reference when you assign it the way that you attempting. It's the same as the jsfiddle where you are assigning the function the reference is not inherited. – Sean Wessell Feb 14 '17 at 17:07
  • It takes the literal function that i have above and assigns that as the handler so there is no more reference to the chained object. – Sean Wessell Feb 14 '17 at 17:08
  • I would appreciate if you can edit the answer to explain _why_ the reference is lost. In languages like C#, C++, Python this would make no sense. Sorry, I'm a bit slow here. – Diego Jancic Feb 14 '17 at 17:18
0

It is acceptable from a syntax standpoint, but doesn't execute properly because of the invocation context. If you reference the function in this way (rather than wrapped inside another function that is invoked via the element) the this binding won't be mapped properly and therefore the code won't know what to toggle.

Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • This and @SeanWessell answers seem to be correct, but neither explain why the `this` binding or context is lost. Coming from C#, this makes no sense. Thanks. – Diego Jancic Feb 14 '17 at 17:14
  • 1
    'this' in JavaScript is vastly different from other languages. In JavaScript, it is volatile. The object that 'this' binds to is not based on where 'this' appears in the code (as is the case in C#). It is determined by the object that is in scope that triggers the code. – Scott Marcus Feb 14 '17 at 18:13
  • See this: http://stackoverflow.com/questions/41496958/this-does-not-work-properly-in-another-event-im-clueless-as-to-why/41496969#41497008 for another answer that I wrote that explains this in more detail. – Scott Marcus Feb 14 '17 at 18:16
  • Here is another answer of mine that also closely relates to this: http://stackoverflow.com/questions/41385835/invocation-context-and-execution-context-in-javascript-are-we-talking-of-th/41386097#41386097 – Scott Marcus Feb 14 '17 at 18:17