JSLint错误:意外的'this'

43

我对以下代码中为什么JSLint对我的this使用感到惊讶感到困惑:

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

对于这两个属性分配,JSLint报告说:Unexpected 'this'。 我该如何纠正我的代码?


你的 this 不一定是错误的。testConstr 用作构造函数了吗?你使用 call 还是 apply 调用它? - Oriol
如果你觉得JSLint有太多的任意警告,那么你可能要考虑一下 JSHint。 - Alexander O'Mara
如果将其大写为TestConstr,会发生什么?警告是否仍然存在? - apsillers
1
@apsillers 是的,那也是我的第一步。但还是失败了。新的 JSLint 希望鼓励你完全停止使用 this。我是说,诚然,作用域确实是对于新(和老!)JavaScript 程序员最令人困惑的话题之一,但我不知道这是否足以成为默认禁止使用它的理由。他的理由是“在语言中使用 this 使得谈论该语言更加困难”。我猜这并没有错。或许值得一试一段时间。 - ruffin
1
JSLint现在提供了一个选项来抑制此类投诉。请参见我的答案 - DavidRR
6个回答

35

你的代码可能完全正确(也可能有问题,取决于你如何调用testConstr)。

我的建议是:告诉JSLint别再管闲事了

在此输入图片描述

或者干脆不使用JSLint。


3
我会称之为使用 new 关键字(即将其用作对象构造函数)。如果这不太清楚,我很抱歉。换句话说,JSLint 并不自动要求我使用构造函数模式?对像我这样的新手来说不是很有帮助... - jdw
@jdw 如果你使用 new,那应该是完全没问题的。JSLint 只是过于谨慎了。 - Oriol
1
是的,那也是一种选择,但如果我不想使用这个JSLint指令来抑制这个警告呢? - Shashwat Tripathi

32

换句话说,JSLint 不会自动期望我使用构造函数模式吗?

你知道吗,我认为你是对的。你的问题困扰了我,我加入了Crockford 的 JSLint 讨论组并且提出了问题。他回答了我,但忽略了我下面要介绍的解决方案,我认为这意味着没问题,就像 JSLint 不会抱怨某些情况一样。

(我还在等待更新版的《JavaScript高级程序设计》。)

除了这个警告之外,以下是我建议用于通过 Beta JSLint 的面向对象 JavaScript 的方法(至少截至今天)。

我将重写 MDN 页面中的示例,《介绍面向对象编程》,该示例本身广泛使用了this

使用 this

以下是来自上面链接的 MDN 示例的原始未经 JSLint 处理的代码:

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"

遵循我们熟知和热爱的约定。

没有this

很容易想出不遵循该模式的“构造函数”,但如果我没有漏掉什么,我们将失去使用prototype的功能,并且必须在我们的构造函数中包含所有我们希望所有 Peep共享的对象方法。

/*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();

所以有一种可供检测的替代方案。除了 return peep; 和方法内部的内部定义之外,它使 JavaScript 的行为像你可能遇到的许多面向对象语言一样。至少它不是错误的。

没有访问 prototype 并不可怕;在不紧贴构造函数旁边改变 prototype 是非常糟糕的消息,因为你的代码会变得混乱。“某些 Person 具有 sayGoodbye(),而有些则没有,这取决于我们是否在他们构建时修改了原型。”那很糟糕。因此,这种替代约定具有其优点。

当然,您仍然可以在单个实例化的 Peep 中添加函数,但我不确定如何在不使用 this 的情况下访问 firstName,因此也许他希望我们停止在构建之后更改对象。

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

(我的意思是,我们也可以在处理过程中仍然对Peep进行猴子补丁来改变它,但那太可怕了,愚蠢的编程方法。通常情况下。)

继承(无需this

我认为继承很容易。

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.

编辑:请参考这个回答,其中提问者后来发现了Crockford建议的创建OO JavaScript的方法。我正在试图说服那个人删除那个Q&A并将A移动到这里。如果他不这样做,我可能会将他的东西添加到这里并将其变为社区wiki。


编辑:请看MDN上的这篇文章,解释为什么它能工作:

(通常构造函数不会返回值,但如果他们想要覆盖正常的对象创建过程,则可以选择这样做。)


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;

}());

3
在严格模式下,this引用被设置为undefined。因此,你的两个语句都将导致读取undefined对象的属性,这将导致异常。如何更正代码?删除这两行即可。更新:我上面说的是JSLint如何处理你的代码,而不是我如何处理。

1
或者:因为在非严格模式下,这样的 this 指的是 window,所以 window.jokerwindow.x 也是可能的……除非 this 指向一个事件目标或其他什么东西…… - Sebastian Simon
3
你假设 testConstr 被称为 testConstr() 来调用,但考虑到它的名称,这不一定是情况。 - Oriol
2
@Oriol 我并不假设任何东西 - 是 JSLint 这样做的。我个人认为 JSLint 更具破坏性而非实用性。 - zerkms
1
回复:破坏性:值得指出的是,this选项是JSLint beta的新功能,该版本只有几周的历史。我也有点失望。我的意思是,Crockford的评论,“*[使用this]就像与Abbott和Costello一起进行编程”并没有真正有助于指出在这种情况下什么是最佳实践。我现在给beta版以怀疑的好处,但请注意,原始代码按原样编写,在old.jslint.com上进行了语法检查,只需将“混乱的空格”指令设置为true即可。所以上个月更少的罪恶。 - ruffin

3
JSLint报错:意外出现了'this'。我该如何修改代码?
无需修改您的代码。
在JSLint的帮助页面中,在/*jslint*/指令的部分中,已经添加了一个"容忍this"选项到可用选项表中。
+---------------+------+---------------------------------+
| Tolerate this | this | true if this should be allowed. |
+---------------+------+---------------------------------+

因此,为了抑制关于使用this的投诉,请在第一条语句之前将以下指令放入您的源文件中:
/*jslint
    this
*/

请注意,在选项 this 后面可以通过在选项之间插入逗号来添加其他/*jslint*/ 选项。请参阅 JSLint 帮助页面。
另外,请参见 @Oriol 的答案,以在 JSLint 用户界面中启用"容忍 this" 选项。

1

我知道这是一个老问题,但如果有帮助的话,我观看了Douglas Crockford的演讲(大约在23分钟处),他说他将其删除是因为攻击者可以将一个方法作为函数运行,并使用“this”关键字访问全局范围。

他还表示这也意味着不再使用Object.create - 这是他帮助引入到语言中的一个特性!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接