42

Having trouble understanding why JSLint is surprised by my use of this in the following code:

function testConstr (x) {
    'use strict';
    this.joker = "Whyyy sooo seriousss?";
    this.x = x;
}

For both property assignments, JSLint says: Unexpected 'this'. How do I correct my code?

jdw
  • 3,755
  • 3
  • 17
  • 16
  • Your `this` is not necessarily bad. Is `testConstr` used as a constructor? Do you call it with `call` or `apply`? – Oriol May 19 '15 at 00:41
  • If you find JSLint to have too many arbitrary warnings, you way want to look into JSHint. – Alexander O'Mara May 19 '15 at 00:51
  • What happens if you capitalize it to `TestConstr`? Does the warning remain? – apsillers May 19 '15 at 01:56
  • 1
    @apsillers Yep, that was my first move too. Still busted. The new JSLint wants to encourage you to stop using `this` altogether. I mean, admittedly, scope really is one of the most confusing topics for new (and old!) JavaScript coders, though I don't know that that's enough reason to ban its use by default. His [rationale](http://jslint.com/help.html) is "Having `this` in the language makes it harder to talk about the language". That's not *wrong*, I guess. Probably worth trying for a while. – ruffin May 19 '15 at 13:00
  • 1
    JSLint now offers an option to suppress this complaint. Please see [my answer](http://stackoverflow.com/a/30603515/1497596). – DavidRR Jun 02 '15 at 18:13

6 Answers6

35

Your code might be perfectly correct (it might also be problematic, depending on how you call testConstr).

My suggestion is: tell JSLint to shut up

enter image description here

Or don't use JSLint at all.

Oriol
  • 274,082
  • 63
  • 437
  • 513
  • 3
    I would call it using the `new` keyword (i.e. use it as an object constructor). Sorry if that wasn't clear. So in other words, JSLint doesn't automatically expect me to use a constructor pattern? Not helpful for nubes like me . . . – jdw May 19 '15 at 00:50
  • @jdw If you use `new` it should be perfectly fine. JSLint is more than paranoid. – Oriol May 19 '15 at 00:52
  • 1
    Yeah, that is also one option but what if I don't want to suppress this warning by using this JSLint directive. – Shashwat Tripathi Apr 18 '17 at 12:46
32

So in other words, JSLint doesn't automatically expect me to use a constructor pattern?

You know, I think you're right. Your question bugged me, and I signed up for Crockford's JSLint discussion group and asked. He replied, but ignored the solution I'm going to present, below, which I think means that it's okay, the same way JSLint doesn't complain if something passes muster.

(I'm still waiting for an updated Good Parts, though.)

That caveat aside, here's what I'd suggest doing for OO JavaScript that passes Beta JSLint (as of today, anyhow).

I'm going to rewrite an example from MDN's page, "Introduction to Object Oriented Programming," which itself uses this liberally.

With this

Here's the original, unlinted MDN example from the section linked, above:

var Person = function (firstName) {
  this.firstName = firstName;
};

Person.prototype.sayHello = function() {
  console.log("Hello, I'm " + this.firstName);
};

var person1 = new Person("Alice");
var person2 = new Person("Bob");

// call the Person sayHello method.
person1.sayHello(); // logs "Hello, I'm Alice"
person2.sayHello(); // logs "Hello, I'm Bob"

That follows the conventions we know and love.

Without this

It's pretty easy to figure out how to make "constructors" that don't follow that pattern, but we lose use of prototype, if I'm not missing something, and have to include all of the object's methods in our constructor that we want all of our Peeps to share.

/*jslint white:true, devel:true */
var Peep = function(firstName) {
    "use strict";
    var peep = {};
    peep.firstName = firstName;

    peep.innerSayHello = function() {
        console.log("Hello, I'm " + peep.firstName + ".");
    };

    return peep;
};

var peep1 = new Peep("Bob");
var peep2 = new Peep("Doug");

peep1.innerSayHello();
peep2.innerSayHello();

So there's a lintable alternative. That does, other than the return peep; and the inner definition of methods, make JavaScript act like many OO-first languages you might encounter. It's not wrong, at least.

Not having access to prototype isn't horrible; it's really bad news to change prototype somewhere that's not right beside the constructor, as your code would go to spaghetti. "Some Persons have sayGoodbye() and some don't, depending on if we'd amended the prototype at the point of their construction." That's awful. So this alternative convention has its advantages.

You can still, of course, add functions to a single instantiation of Peep later, but I'm not sure how you'd access firstName without using this, so perhaps he wants us to stop munging objects after construction.

person1.sayGoodbye = function (other) { 
    console.log("Goodbye, " + other + "."); 
};

(I mean, we could also still monkey-patch Peep to change it mid-process, but that's horrible, stupid programming. Usually.)

Inheritance (without this)

And inheritance is easy enough, I think.

var PeepWithGoodbye = function (firstName) {
    "use strict";
    var peepWithGoodbye = new Peep(firstName);

    peepWithGoodbye.innerSayGoodbye = function (otherPeep) {
        if (undefined === otherPeep) {
            otherPeep = { firstName: "you" };
        }
        console.log("This is " + firstName 
            + " saying goodbye to " + otherPeep.firstName + ".");
    };

    return peepWithGoodbye;
};

var pwg1 = new PeepWithGoodbye("Fred");
pwg1.innerSayHello();           // Hello, I'm Fred.
pwg1.innerSayGoodbye(peep1);    // This is Fred saying goodbye to Bob.
pwg1.innerSayGoodbye();         // This is Fred saying goodbye to you.

EDIT: See also this answer where the asker later found Crockford's suggested means of creating OO javascript. I'm trying to convince that guy to delete that Q&A and move the A here. If he doesn't, I'll probably add his stuff and community wiki it here.


EDIT: See this from MDN for why it works:

(Normally constructors don't return a value, but they can choose to do so if they want to override the normal object creation process.)

Community
  • 1
  • 1
ruffin
  • 16,507
  • 9
  • 88
  • 138
4
'use strict';

var SudoConstructor = (function () {

    /* We need bind, < IE9 needs a (tiny) polyfill */

    function protoEsqDeclare(sudoThis) {
        return sudoThis;
    }

    function protoEsqSet(sudoThis, newValA, newValB) {
        sudoThis.valA = newValA;
        sudoThis.valB = newValB;
    }

    function instanceCreator(valA, valB) {
        var sudoThis = {
            valA: valA,
            valB: valB
        };

        return {
            declare: protoEsqDeclare.bind(null, sudoThis),
            set: protoEsqSet.bind(null, sudoThis)
        };

    }

    return instanceCreator;

}());
Lee
  • 41
  • 2
3

In a strict mode this reference is set to undefined.

So both your statements will cause reading properties of the undefined object, which will lead to an exception.

How do I correct my code?

Remove both those lines.

UPD: what I state above is how JSLint treats your code, not how I do that.

zerkms
  • 249,484
  • 69
  • 436
  • 539
  • 1
    Or: because in non-strict mode such a `this` refers to `window` a `window.joker` and `window.x` is also possible… unless the `this` refers to an event target or something… – Sebastian Simon May 19 '15 at 00:42
  • 3
    You assume `testConstr` is called as `testConstr()`. Given its name, it may not be the case. – Oriol May 19 '15 at 00:42
  • 2
    @Oriol I don't assume anything - it's JSLint that does. I personally find JSLint more destructive than useful. – zerkms May 19 '15 at 00:45
  • 1
    Re: destructive: It's worth pointing out the `this` option is new to JSLint beta, which is just a few weeks old. I'm a bit disappointed too. I mean, [Crockford's comment](http://jslint.com/help.html) that, "*[Using `this`] is like pair programming with Abbott and Costello*" isn't real helpful for pointing out what *is* best practice in *exactly* this situation. I'm giving the beta the benefit of the doubt for now, but **note that the original code, as written, lints in [old.jslint.com](http://old.jslint.com/) with just the "messy white space" directive set to true.** So less evil last month. – ruffin May 19 '15 at 12:55
2

JSLint says: Unexpected 'this'. How do I correct my code?

There is no need to correct your code.

In the help page for JSLint, in the section for the /*jslint*/ directive, a "Tolerate this" option has been added to the table of available options:

+---------------+------+---------------------------------+
| Tolerate this | this | true if this should be allowed. |
+---------------+------+---------------------------------+

So, to suppress complaints about the use of this, place the following directive into your source file before the first statement:

/*jslint
    this
*/

(Note that other /*jslint*/ options may follow this by inserting a comma between options. See the JSLint help page.)

Also see the answer by @Oriol to enable the "Tolerate this" option in the user interface for JSLint.

Community
  • 1
  • 1
DavidRR
  • 18,291
  • 25
  • 109
  • 191
1

An old question I know, but in case it helps anyone, I was watching a talk by Douglas Crockford in which he says (at about 23mins) that he took it out because an attacker could run a method as a function and get access to the global scope with the 'this' keyword.

He says it also meant not using Object.create anymore either - a feature he helped introduce to the language!

Leon Segal
  • 679
  • 7
  • 28