1

I've stumbled over a problem. I have an object method foo defined as:

var obj = {
    foo: function() {
        $('.personName').mouseover(function() {
            this.highlight($(this).attr('faceIndex'));
        });
    }
}

So what should happen is that whenever the mouse cursor is over an HTML object of type personName, the obj.highlight method should be called with the faceIndex value from the HTML object as an argument. However I apparently have a clash between two this's: the one of jQuery and the one of JavaScript (referencing to obj from inside obj).

What can (should) I do? Have I violated some good programming practice?

texnic
  • 3,535
  • 4
  • 30
  • 68
  • `this` is one of the biggest pain points in JS. You'll have to assign the obj this to a local variable so that you can use that it in the inner function. – Douglas Nov 24 '14 at 23:18
  • 1
    Don't get confused. When you wrap things in jQuery $() function `this` becomes a jQuery object. Just to shed some lights: http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/#When_this_is_most_misunderstood_and_becomes_tricky and http://stackoverflow.com/a/26644821/1672895 – carlodurso Nov 24 '14 at 23:38
  • possible duplicate of [JavaScript "this" keyword](http://stackoverflow.com/questions/3127429/javascript-this-keyword), especially [this answer referring to jQuery callbacks](http://stackoverflow.com/a/3127609/7469). – Mathletics Nov 25 '14 at 00:17
  • @Mathletics: You are right, bur careful search for quite some time didn't reveal that answer. I'd suggest you add this link as one more answer here, for better findability. – texnic Nov 25 '14 at 06:48

3 Answers3

2

A typical pattern to work around this is to use a local variable to store the first this:

var obj = {
    foo: function() {
        var _this = this;
        $('.personName').mouseover(function() {
            _this.highlight($(this).attr('faceIndex'));
        });
    }
}

Using a language like TypeScript or an ES6 compiler makes it easier to use this pattern without having to write the _this by hand each time.

Douglas
  • 32,530
  • 8
  • 68
  • 88
  • 2
    why? he already has a reference to `obj` right there. Also, if you're going to use that pattern, [please give variables meaningful names](http://togakangaroo.github.io/2013/04/29/stop-that-equals-this-ing). – George Mauer Nov 24 '14 at 23:26
  • but naming things is [one of the hardest problems in computer science](http://martinfowler.com/bliki/TwoHardThings.html). – Mathletics Nov 25 '14 at 00:15
  • 1
    @George the most meaningful name would be "this", but unfortunately that's already taken. I'd totally recommend using a better language which allows using "this" inside lambdas (even just a more modern version of JS would be fine), but if you've got to work around it by hand, it seems better to do a mechanical workaround that is the same everywhere, rather than to introduce new variable names each time. Typically I'd expect "obj" inside a function to refer to some other obj, not "this" obj, so re-using that name seems like it could lead to unnecessary confusion. – Douglas Nov 25 '14 at 06:21
  • Coming back to this (didn't pop up in my inbox) I guess I'll reply with [this blog post I wrote](http://togakangaroo.github.io/2013/04/29/stop-that-equals-this-ing). When you have a choice, you can almost always find a more descriptive name for a variable than `this`. – George Mauer Jan 03 '15 at 20:20
  • I think we're going to have to agree to disagree here, I'm advocating for meaningful names, and you're advocating for descriptive names. Those two strategies are not always going to overlap. – Douglas Jan 04 '15 at 10:12
1

Short answer: do

    $('.personName').mouseover(function(event) {
        obj.highlight($(event.target).attr('faceIndex'));
    });

Longer explanation:

Javascript doesn't really have a concept of this. At least not in the way you're used to thinking of it. Oh there's a keyword alright, and it kind of does the thing you expect a lot of the times but it doesn't work the way that you probably think.

The fact of the matter is that in javascipt, this is no different than any other parameter. Let me show you.

Most people are aware that in javascript you can invoke functions like this doSomething(param1, param2) or like this doSomething.call(null, param1, param2). If you wanted, you can write all function invocations using .call

See that null there? Anything you pass in there is what this gets set to.

doSomething.call(null, param1, param2);
doSomething.call(obj, param1, param2);
doSomething.call(window, param1, param2);
doSomething.call("foobar", param1, param2);

If you don't use .call the runtime just takes a guess at what value you want there.

So given this, consider that the only difference between this and any other parameter is that you don't get to give this a name! Your problem is that you have two function scopes and the inner one has a variable named this which hides the outer one's this.

Solution: don't use this. Most libraries in fact (jquery included), don't force you to use this and also pass in the value as a regular parameter

    $('.personName').mouseover(function(event) {
        obj.highlight($(event.target).attr('faceIndex'));
    });

ambiguity solved!

Avoid using this in JavaScript, if at all possible. It is almost never necessary.

texnic
  • 3,535
  • 4
  • 30
  • 68
George Mauer
  • 103,465
  • 117
  • 349
  • 581
  • 1
    I thought the argument for an event handler was an `Event`, not a jquery-object containing the node (per your example)? – Mathletics Nov 24 '14 at 23:53
  • excellent point. `el` is passed usually to map and each and filter. I forgot `on` is different. Fixed – George Mauer Nov 24 '14 at 23:55
  • Short-answer is good (minus the typo in `target`), but the long answer here is a lot of _weird_ opinion about `this`, and the last line strikes me as pretty inflammatory. – Mathletics Nov 25 '14 at 00:02
  • @Mathletics I have lots of *weird* opinions, that's true. "Avoid using `this`" is an opinion. What is not an opinion is that in Javascript, `this` is fundamentally a parameter. That is objective as is made clear by the functionality of `.call`. My opinion comes from connecting the dots. If `this` is fundamentally no different from any other parameter, and it is one of the most confusing concepts in javascript, why on earth use it over a regular parameter? – George Mauer Nov 25 '14 at 02:28
1

this in javascript is a very difficult thing to understand in callbacks because it may refer to virtually any instance. And that is because the callback is called from a different context.

The long story : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

As an alternative to previous answers :

One way I prefer dealing with it is by using a bind, also known as a proxy (in JQuery). jQuery has one implemented here : jQuery.proxy.

It has the benefit of letting you chose who is your this in the callback function.

For example:

var obj = {
    foo: function() {
        $('.personName').mouseover($.proxy(function(event) {
          // this refers here to obj instance
          console.log(this);
          // event is a jQuery decorated that holds the reference of your element 
          console.log(event);
        }, this));
    }
};

And the true benefit of it, is that it lets you construct components that don't have "ugly" nested callback anonymous functions:

var obj = {
    foo: function() {
        $('.personName').mouseover($.proxy(this.mouseOverCallback, this));
    },
    mouseOverCallback : function(event) {
       // this refers here to obj instance
       console.log(this);
       // event is a jQuery decorated that holds the reference of your element 
       console.log(event);
    }
};
Tiberiu C.
  • 2,943
  • 26
  • 37
  • good point, but I would clarify that `proxy` is jquery-specific terminology. The more general concept is `.bind`. At this point, this function exists by default on any function in any remotely modern browser. – George Mauer Nov 24 '14 at 23:51
  • Thanks for pointing that out, I'll look into it cause I didn't knew, An honest marketing phrase for JQuery would be : "JQuery, shading native JavaScript since 2006." – Tiberiu C. Nov 25 '14 at 00:05