为什么在JavaScript构造函数中使用副作用是不好的实践?

11

通常我在代码中使用类似于设计模式定制对象的东西。

但是JSLint不赞成使用这样的结构:

function MyClass() { this.init(); }
new MyClass(data);

由于对象在创建后立即被丢弃 - 它没有被用于任何事情上。 我们可以通过将其赋值给一个变量来骗过JSLint,但这并不改变JSLint(以及我猜许多JavaScript爱好者)不鼓励此模式。

那么为什么在JavaScript构造函数中使用副作用被视为不良实践呢?

就我所知,我认为这是一种好的实践,因为:

  1. 你只有一个设置函数,因此如果你要管理一个MyClass实例列表以供稍后访问,维护应该更容易。(将对象推入数组是一种副作用,你必须在构造函数返回后才能这样做才能符合“良好实践” = 更难维护。)
  2. 它有自己的原型,因此拥有"类所有权":Firebug将其报告为MyClass的一个实例而不仅仅是Object。(在我看来,这使它优于其他设计模式。)

JSLint 不鼓励使用 new MyClass,因为在实例化后你并没有使用它。因此,它只被用于其副作用。相反,这个例子可以重写以利用依赖注入,如 initialize(new MyClass());(尽管这个例子太简单了,有点傻)。 - zzzzBov
你真的从未将新创建的 MyClass 实例分配给任何东西吗(换句话说,这是一种使 init() 成为一种静态方法的复杂方式吗)?“正常”使用此模式不会触发 JSLint 警告。 - Frédéric Hamidi
@FrédéricHamidi 并不是从不会有副作用,而是有时候它会自己完成工作,也就是说,没有什么需要额外做的了。一个 Person 构造函数可能会实例化一个人对象,但你现在不一定需要对这个人进行操作。如果你确实需要,副作用会在数组中注册自己以供稍后访问。 - user1994380
@zzzzBov,如果不鼓励使用副作用,但仅将其用于副作用,则JSLint的警告可能会误导。警告内容为“不要将'new'用于副作用。”我可能会弄错,但对我来说,这意味着它必须专门用于设置属性(或继承)。 - user1994380
构造函数可以用于执行功能,但是通常期望你不仅仅只是实例化一个构造函数。如果你所做的只是实例化一个对象并将其丢弃,最好使用普通函数调用。 - zzzzBov
1个回答

11
在他的书《Clean Code》中,Robert Martin说:

副作用就是谎言。你的函数承诺做一件事,但它同时也做了其他隐藏的事情...它们是狡猾和有害的虚假陈述,通常会导致奇怪的时间耦合和顺序依赖。

根据您在数组评论中所描述的内容,听起来像是一种“奇怪的时间耦合”。

1
感谢提供有用的答案,以及相关搜索术语和参考材料。我找到了这个与任何有类似问题的人相关的资源 - Mark Seemann's .NET blog(不使用JavaScript)。除非有更好的答案,否则我将接受这个答案。 - user1994380

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