Javascript/jsLint: What to replace jQuery(this) with when using "use strict";
Asked Answered
K

4

23

When I validate the following code with jslint I get the following errors.

function displayMegaDropDown() {
"use strict";
var liMegaPosition, divMegaOffset;
liMegaPosition = jQuery(this).position();
divMegaOffset = { top: liMegaPosition.top + jQuery(this).height(), left: liMegaPosition.left };
jQuery(this).find("div").offset(divMegaOffset);

jQuery(this).addClass("hovering");
}

Problem at line 4 character 29: Strict violation.

 liMegaPosition = jQuery(this).position();  

Problem at line 5 character 56: Strict violation.

divMegaOffset = { top: liMegaPosition.top + jQuery(this).height(), left: liM...

Problem at line 6 character 12: Strict violation.

jQuery(this).find("div").offset(divMegaOffset);

Problem at line 8 character 12: Strict violation.

jQuery(this).addClass("hovering");

I'm guessing that it's because of the use of jQuery(this) but I don't understand what to replace it with. Note that this is not because jQuery isn't declared as a global.

Kussell answered 4/9, 2011 at 14:55 Comment(3)
Read here: #2501012Reflector
Here, I refactored your code a bit: jsfiddle.net/Kj3WR/1Reflector
Thanks @sime-vidas. The linked question did help, sort of, but didn't explain why it was useful.Kussell
J
20

I think the problem is that you use this not inside of a method. The following code

/*global jQuery */
var myObj = {
    myNethod: function displayMegaDropDown() {
        "use strict";
        var ts = jQuery(this),
            liMegaPosition = ts.position(),
            divMegaOffset = {
                top: liMegaPosition.top + ts.height(),
                left: liMegaPosition.left
            };

        ts.find("div").offset(divMegaOffset);

        ts.addClass("hovering");
    }
};

or this one

/*global jQuery */
function displayMegaDropDown(t) {
    "use strict";
    var ts = jQuery(t),
        liMegaPosition = ts.position(),
        divMegaOffset = {
            top: liMegaPosition.top + ts.height(),
            left: liMegaPosition.left
        };

    ts.find("div").offset(divMegaOffset);

    ts.addClass("hovering");
}

will give you no errors or warnings.

UPDATED: One more version, which is very close to your original one, also has not errors or warnings:

/*global jQuery */
var displayMegaDropDown = function () {
    "use strict";
    var ts = jQuery(this),
        liMegaPosition = ts.position(),
        divMegaOffset = {
            top: liMegaPosition.top + ts.height(),
            left: liMegaPosition.left
        };

    ts.find("div").offset(divMegaOffset);

    ts.addClass("hovering");
};

UPDATED 2: I find the question interesting for understanding. So I look through ECMAScript standard . I find the following in the "Annex C (informative) The Strict Mode of ECMAScript" (see better in HTML version here):

If this is evaluated within strict mode code, then the this value is not coerced to an object. A this value of null or undefined is not converted to the global object and primitive values are not converted to wrapper objects. The this value passed via a function call (including calls made using Function.prototype.apply and Function.prototype.call) do not coerce the passed this value to an object (10.4.3, 11.1.1, 15.3.4.3, 15.3.4.4).

I suppose that it is the reason of the JSLint error.

Of cause if you would switch off the strict mode the code will have no more errors:

/*global jQuery */
/*jslint sloppy: true */
function displayMegaDropDown() {
    var ts = jQuery(this),
        liMegaPosition = ts.position(),
        divMegaOffset = {
            top: liMegaPosition.top + ts.height(),
            left: liMegaPosition.left
        };

    ts.find("div").offset(divMegaOffset);

    ts.addClass("hovering");
}

UPDATED 3: It seems that impossibility of the usage of this in the function statement and the possibility of the usage of this in the function expression seems suspected for many people. Today I received one more comment about this. So I created very simple demo

/*jslint devel: true */
(function () {
    'use strict';
    function test() {
        alert(this);
    }

    test();
}());

You can test it here that in IE9 the demo display alert with the text "[object Window]" and on the current versions of Chrome and Firefox you will see "undefined".

So the problem with the usage of this in function statement is not "a jslint thing". It's real problem which you should take in considerations during development of your JavaScript programs.

I personally prefer to use function expression and almost never use more function statements. I think that the people who come from another program languages (like me too) try at the beginning to use the same construction which was good in your favorite languages. Only later one finds out that definition of variables in block is bad (there are no block level scope in JavaScript) and that function statements is not always the best choice too.

Julianajuliane answered 4/9, 2011 at 16:32 Comment(13)
+1 for providing alternatives that appease jsLint, which I'm certain is what OP was after. In response to your inquiry under my answer, I honestly don't spend too much time wondering why jsLint does what it does because I never use it. But with regard to the part of the spec you referenced, it still isn't a "strict violation" as jsLint claims. I suppose if jsLint can't tell what the value of this will be, it could complain that you may be passing undefined to jQuery() via this when in strict mode, but it's still a stretch to call it a violation. The odd thing is with your first...Larsen
...update where you just changed the function declaration to an expression. There's no effective difference in the code being analyzed, yet it fixed the "issue". That just seems weird to me. I certainly would not take the approach of removing the "use strict"; declarative. I think all code should be evaluated in strict mode if at all possible. IMO, strict mode should pretty well supplant the useful parts of jsLint. Anyway, good answer. :)Larsen
@patrick dw: Thanks for your comments. I included the example, where the strict mode was off, only to verify that the error message of JSLint exists really only because of the usage strict mode. I personally use "use strict" always. I use also JSLint permanently to find small errors in my code. I find JSLint very helpful, but find only some of errors in style of coding not good (like about "i++" in for (i=0; i<l; i++)). I asked about your opinion mostly because I am not sure that I interpret the referenced fragment of the ECMAScript specification correctly. I know English not good enough.:-)Julianajuliane
After a pile of research I can now see that both @patrick-dw and oleg are correct but oleg's answer appeases jslint. If js calls a method normally, in sloppy mode, then this refers to the global object (the window in a browser). In strict mode this is disabled as oleg pointed out. However, one can call the function with call or apply in order to set this, and event handlers will also set this. For my code, the hoverIntent plugin was calling apply to set this to the element to which the event applied. oleg's update solves the problem but patrick-dw is correct that there is no difference.Kussell
@Sarge: I am glad that I could help you.Julianajuliane
I'm late to the party, but if anyone can explain why a function expression allows "this", but not a function declaration, I'd be pretty interested... :)Wreckfish
@ChrisFrancis: I understand as so: var MyClass = function () { this.myProperty = "smomething"; }; must work because of such kind of defining of the constructor of the new object (new class definition). One can use var myVar = new MyClass(); to define the instance of the class which call the constructor. The question is only why Strict Mode of ECMAScript decided explicitly remove the this support. I think that one wished to be able to define the function which has no relation to any object.Julianajuliane
Ah ok, thanks! So yeah, because a function expression is syntactically identical to a valid constructor, it must be allowed. That seems like an inherent 'bug' in the Javascript API to me - converting a function declaration to an expression allows you to 'get around' the strict mode violation, without actually changing the context this is evaluated in...Wreckfish
@ChrisFrancis: Exactly! Last time I used to use var MyClass = function () {} version of definition of the functions because I like to be able to define some context information in this. By the way if you would look through my post here you would find what Douglas Crockford think about this: "Your code is horrible. I wish JSLint rejected all of that". I take it easy and stay at my opinion. :-)Julianajuliane
@ChrisFrancis: Nevertheless I see not so many possibilities. If one writes plugin and one needs to call a callback somewhere deep in the code, which the user of the plugin provides as parameters, one have to use usercallback.call(mainObject, parameters) to set additionally this context which could be helpful for the user. The user should just use var usercallback = function () or anonymous style of declaration of the callback to be able to use information from this. The same problems exist internally in long code. So I 'll continue to use the way independent from the Douglas' opinion.Julianajuliane
My suspicion is that complaining about the use of this in top level functions is a jslint thing, not a strict mode thing.Valance
@SeanMcMillan: NO, it isn't. I wrote "UPDATED 3" part of my answer specially to answer on your comment.Julianajuliane
@Oleg:But it's legitimate javascript to use this in a function statement. Foolish, sure. Probably not what you want, sure. worth warning about in a lint program, sure. But not an actual error. If it was, then the javascript interpreter would throw an "invalid use of this in a function statement" exception. And it's just as broken in a function expression, it's just that jslint can't tell legit uses from broken uses inside a function expression. Changing from a statement to an expression silences jslint, but does not improve your code any.Valance
L
2

There is no strict mode violation in your code. If there was, you'd get an Error of some sort.

Larsen answered 4/9, 2011 at 15:0 Comment(2)
Your opinion about my answer would be interesting for me.Julianajuliane
Responded under your answer. :)Larsen
A
1

By native javascript, this will be undefined in strict mode if the function contains it is not invoked either:

  • as a method of an object
  • by Function.call or Function.apply
  • as a constructor

That's the reason jshint is complaining about it.

However, if the function is being used as a event handler for jQuery, jQuery will force the $(this) as the jQuery object of the DOM that fires the event.

To prove this, open the console of this page and run the following code.

(function(){
    "use strict";
    function event_handler() {
        $(this).css('background','red');
    }

    $('#content').on('click', 'table', event_handler);
})();

Click any question or answer of this page and you would see the background becomes red.

Alexiaalexin answered 10/1, 2017 at 13:33 Comment(0)
P
1

Much easier! Just use ev.currentTarget instead of this. It is the same as you can test with ===

function event_handler(ev) {
    //var $el = $(this); // jshint - error
    var $el = $(ev.currentTarget); // is exactly the same as above
}
Psychic answered 25/2, 2017 at 11:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.