JavaScript中关键字 'new' 有哪些副作用?

21

我正在开发一个jQuery插件,但是我遇到了JSLint错误:

Problem at line 80 character 45: Do not use 'new' for side effects.

(new jQuery.fasterTrim(this, options));

我在搜索JSLint错误或者关于使用new可能产生的副作用方面没有什么好运气。

我尝试通过谷歌搜索“不要将'new'用于副作用”,但是没有结果。 必应给了我2个结果,但它们都只是引用JSLint源代码。希望这个问题能改变这种情况。 :-)

更新#1: 下面是更多的上下文来源:

  jQuery.fn.fasterTrim = function(options) {
    return this.each(function() {
      (new jQuery.fasterTrim(this, options));
    });
  };

更新 #2: 我使用Starter jQuery插件生成器作为我的插件模板,其中包含了该代码。


我已经在 Twitter 上联系了 Starter 的作者 :-) - Pointy
1
查看了“Starter”代码,更新了我的答案 - 真的存在副作用,并且它们被生成的代码使用。 - Pointy
@Pointy,我没有收到你的推特,你把它发送到哪里了?@dougneiner?非常抱歉! - Doug Neiner
没问题 - 你还是来了!我想我发送给了@dougneiner,但无所谓。我并不抱有很高期望,因为我使用Twitter的任何操作似乎都行不通。 - Pointy
6个回答

15

JsLint本身 给出了原因:

构造函数是设计用于使用new前缀的函数。new前缀基于函数的原型创建一个新对象,并将该对象绑定到函数的隐式this参数上。如果您忽略使用new前缀,则不会创建新对象,并且this将绑定到全局对象上。 这是一个严重的错误。

JSLint强制执行约定,即构造函数必须用大写字母开头的名称命名。除非有new前缀,否则JSLint不希望看到以大写字母开头的函数调用。 JSLint不希望在名称不以大写字母开头的函数中看到new前缀,可以使用newcap选项进行控制。

JSLint不希望看到包装形式的new Number、new String、new Boolean。

JSLint不希望看到new Object (应改用{})。

JSLint不希望看到new Array (应改用[] )。


7

Travis,我是“Starter”网站的开发者。

@Pointy说得很对。“Starter”代码之所以这样编写,是因为我们确实需要一个新对象,但此时我们不需要存储对它的引用。

只需将命令从

(new jQuery.fasterTrim(this, options)); 

to

var fT = new jQuery.fasterTrim(this, options);

您已经找到了能够满足JSLint标准的解决方案。

起始插件设置遵循jQuery UI存储元素引用在data中的模式。这就是发生的事情:

  1. 创建新对象(通过new)
  2. 使用jQuery的data将实例附加到DOM元素上:$(el).data('FasterTrim', this)

没有对返回的对象进行使用,因此没有进行var声明。我会研究更改声明并清理输出以便于直接通过JSLint。

更多背景信息

使用data存储对象的好处是,我们可以随时调用$("#your_selector").data('FasterTrim')来访问对象。但是,如果您的插件不需要通过这种方式进行中间处理(即它在一个单一的调用中被设置并且不提供未来交互),则不需要存储引用。

如果您需要更多信息,请告诉我。


好的,谢谢Doug。设置一个变量只会让我得到另一个类似于“未使用的变量:fT”的lint错误,所以我将保持你的Starter代码不变。 - travis
1
如果这真的让你很困扰(当我使用它时,我可能会被归类为JSLint的狂热者),你可以像这样将data调用移动到构造函数之后:var fT = new jQuery.fasterTrim(this, options); $(this).data('FasterTrim', fT); 这样应该就能解决错误了。 - Doug Neiner
除非你想要看到今天的 JSLint 给你一个 Unused 'fT'. 错误。JSLint 的教训是什么?不要成为 JavaScript 时髦人士。;^) - ruffin
“fT”被定义但从未使用,这是怎么回事? - R Claven

6
它在抱怨你调用了“new”,但随后却丢弃了返回的对象。我敢打赌,为什么这段代码要使用“new”?换句话说,为什么它不只是:
jQuery.fasterTrim(this, options);

编辑 好的,那么“Starter”工具生成代码的原因是它确实需要创建一个新对象,并且确实是为了利用副作用。 “Starter”生成的构造函数代码将对新对象的引用存储在受影响的元素上,使用jQuery的“data”功能。


我更新了问题,以展示源代码的周围环境,该源代码起源于Starter代码。 - travis
JS Lint检测到的问题是创建一个新对象并将其丢弃。基本上,jslint假定一个新调用其返回值未被存储必须引起副作用(否则为什么要实例化它),具有副作用的构造函数通常是不良设计。Js lint告诉您fastTrim使用了不良设计。只需使用$.fasterTrim(" Hello "); - Ruan Mendes
@Juan,看起来“Starter”工具生成了那段代码,所以提问者基本上是那个东西的受害者。我刚刚自己也用了那个页面,果然它给出的就是那种模式。我不知道为什么。 - Pointy
嗯,那么有没有一种更安全的方法来以一种 JSLint 批准的方式完成同样的事情呢? - travis
2
也许有更好的方法来解决这个问题,但我不是正式主义者,无法给出建议。如果您正在使用Starter工具(看起来并不那么糟糕),您可能只需要忽略JSLint错误。对我来说,它似乎并不是那么可怕,因为Starter代码恰好做到了这一点。 - Pointy
1
构造函数带有副作用是一件不好的事情,这是一个有趣的观点。我想我可以在函数外将新对象连接到DOM对象,但为此移动一行代码似乎很奇怪。显然,当你创建一个jQuery小部件时,它意味着会产生副作用,所以我认为这是一个无关紧要的问题。哦,对了,我给你点赞 :) - Doug Neiner

6
您正在使用new执行某些操作,而不是创建一个对象并返回它。JSLint认为这是new的无效使用。
您应该像这样使用它:
var x = new SomeConstructor();

或者执行像这样的操作:

SomeMethod();

但是永远不要使用new来执行这样的操作:
new SomeCosntructor(args);

这样做被认为是使用 new 产生副作用,因为你没有用它来创建一个对象。


1
嗯,他正在创建一个对象,但是他却将其丢弃了 :-) - Pointy
我更新了问题,以展示源代码周围的内容,该源代码起源于Starter代码。 - travis
我还是不太明白。我该如何创建类似于Prototype PeriodicalExecuter的东西?推荐的API未通过jslint检查。http://www.prototypejs.org/api/periodicalExecuter - ScottJ
这些准则是最佳实践。Prototype库不遵循这个特定的实践。所以,你必须自己想办法。你可以选择保留错误或将结果分配给本地变量,即使你不会使用它。 - EndangeredMassa

2
基本上,JavaScript 往往运行较慢,因此创建一个新对象只是为了调用一个函数相当低效。该函数本身是静态的。
$.fasterTrim(this, options);

0

来自jQuery fasterTrim源代码

 * Usage: 
 * 
 * $(element).fasterTrim(options);  // returns jQuery object
 * $.fasterTrim.trim(" string ", options);  // returns trimmed string

回答这个问题,“不要使用 new 来产生副作用”的意思是:
不要使用 new 来对其参数执行构造函数会执行的操作,而是仅用于创建对象,因为构造函数中的副作用是不好的!

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