为啥在 Javascript 中不鼓励在 while 语句中赋值?

Posted

技术标签:

【中文标题】为啥在 Javascript 中不鼓励在 while 语句中赋值?【英文标题】:Why is assignment in while statement discouraged in Javascript?为什么在 Javascript 中不鼓励在 while 语句中赋值? 【发布时间】:2020-03-01 09:29:00 【问题描述】:

我刚刚编写了以下运行良好的循环:

let position = 0;
// tslint:disable-next-line:no-conditional-assignment
while ((position = source.indexOf('something interesting', position)) > 0) 
    // do something interesting with something interesting
  position += 1;

但请注意,我必须添加 tslint 例外。显然有人 已决定不允许在 while 有条件的。然而,这是我所知道的最直接的代码模式 特殊情况。

如果这不是“允许的”(或者我应该说“不鼓励”),那该怎么办 推动这个的人认为正确的代码模式是?

【问题讨论】:

They also can be an indicator of overly clever code which decreases maintainability. - 不得不说,你在那里写的东西让我的眼睛流血了,我已经有一段时间了。 palantir.github.io/tslint/rules/no-conditional-assignment 我不知道 correct 代码模式,但以下内容会让你的同事(有点)不那么讨厌你:let position = 0; while(true) if(source.indexOf('something interesting', position) <= 0) break; // do something interesting with something interesting position+=1; @Adam 自 70 年代初以来,我一直在使用这种代码模式,并在 PDP-11 上使用了第一个 C 编译器。将语法写入 K&R 是有原因的:它简洁实用。 【参考方案1】:

正如亚当在 cmets 中的示例,无条件赋值的替代方案可能如下所示:

let position = 0;
while (true) 
  position = source.indexOf('something interesting', position);
  if (position < 0) 
    break;
  
  // do something interesting with something interesting
  position += 1;

如果禁用 curly,语法可能会变得更加紧凑,这会在 if/for/do/while 语句上强制使用大括号,即使 K&R Ch 3 根本不强制使用这些大括号* .这可以很好地比较这些规则的总体动机。 TSLint 在its homepage 上定义了它的用途:

TSLint 是一种可扩展的静态分析工具,用于检查 TypeScript 代码的可读性、可维护性和功能错误。

是否完全可以正确使用无大括号的if 语句,尤其是在您对缩进和格式非常认真的情况下?当然。但是,也完全有可能误用该功能,like in the docs for curly:

if (foo === bar)
    foo++;
    bar++;

您的问题假设“显然有人认为它是不可取的”,但更准确的说法是 这种模式是跨代码库的重要错误来源,足以让 @987654334 的作者@为特定模式提供了内置检测和反馈。该模式不是“不受欢迎的”、“不允许的”或“不鼓励的”,而是足够稀有且被滥用,足以使其检测默认开启

tslint 中的所有规则一样,您可以使用tslint.jsontslint.yaml 来configure the default rule set,并且您和您的团队可以选择禁用curlyno-conditional-assignment 等规则。如果这种情况通常是您的编码风格中的误报(看起来是这样),或者如果您将 tslint 应用于该模式常见且正确使用的现有代码库,这可能特别有用。但是,我认为一个团队声称(正如其他人在这个问题上所做的那样)该模式容易出现=/== 拼写错误和其他错误并且正确的使用不足以证明允许代码库中的模式。


*注意:虽然 K&R 不强制使用大括号,但它在 p56 上确实指出“缩进明确显示了您想要的内容,但编译器没有收到消息,并将 else 与内部 @987654344 相关联@. 这种错误很难找到;当有嵌套的 ifs 时,最好使用大括号。”这表明即使是原始 K&R 文本也支持合理的样式规则或超出其语法支持的限制。

【讨论】:

【参考方案2】:

它使代码的意图更难确定。使用赋值作为表达式是不寻常的 - 如果你在一个条件中看到它,你有多确定代码的作者打算使用赋值,或者它可能是错字?如果代码编写者打算比较这个值怎么办? EG

while ((position = source.indexOf('something interesting', position)) > 0) 

while ((position == source.indexOf('something interesting', position)) > 0) 

这两者非常不同。 (在这种特殊情况下,Typescript 会警告您与布尔值和数字的比较可能是一个错误,但在 javascript 中不会发生这种情况,在其他一些情况下也不会发生这种情况。)

对于上面的代码,您可以可能使用正则表达式,而不是在条件内部分配,在条件外部分配,甚至更好(尽管在不知道内容的情况下无法确定) // do something interesting with something interesting)。

不幸的是,它看起来很重复,但另一种选择是

let position = 0;
const getPos = () => 
  position = source.indexOf('something interesting', position);
;
getPos();
while (position > 0) 
  // do something interesting with something interesting
  getPos();

【讨论】:

您的第二个示例甚至无法在 Typescript 中编译。我认为这是一个合理的假设,即代码的作者并不打算让它不编译。 这些类型的模式,你想运行一些东西,然后运行一个循环体,如果这个东西是真的,是 one 在条件内赋值可能有意义的地方.当您还需要每个匹配项的捕获组(至少在.matchAll 可用之前)使用全局正则表达式迭代字符串时也会发生这种情况。禁用规则可能有时可以让代码更优雅,但通常可以使用更好的模式来代替条件内的赋值。 @AlanObject 那是因为代码是 javascript,而不是 Typescript。对于 Typescript,您需要指定 getPos 的返回类型:function getPost():number ... @slebetman Typescript 编译器几乎总是足够聪明,能够自动确定返回类型(有些人更喜欢使用该自动过程而不是每次都显式指定它 - 智能感知可以使用任何一种方式)。必须指定参数类型,但如果没有参数,则也不需要参数类型 @CertainPerformance Typescript 会抱怨您正在尝试对布尔值进行 &gt; 比较【参考方案3】:

然而,对于这种特殊情况,这是我所知道的最直接的代码模式。

其实不然。更直接和更容易阅读的模式如下:

let position = 0;
while (position >= 0) 
    // do something interesting with something interesting
  position = source.indexOf('something interesting', position) + 1;

上面的代码有效,因为如果找不到字符串,indexOf 会返回 -1

【讨论】:

循环的第一遍没有position 的可用值。您必须添加一个 if 语句才能完成这项工作。 循环的第一遍确实对position有效,它是let position = 0 让它工作的关键是&gt;=而不是&gt;&gt; 是行不通的 恐怕你没有抓住重点。在循环版本的第一遍中,position 的值为 0。该值不指向 source 字符串中包含字母 'something interesting' 的位置。事实上,source 字符串可能根本没有这种模式,但您的循环将至少执行一次。因此,为了使循环中的任何逻辑都能正常工作,它必须以不同的方式处理这种特殊情况。换句话说,额外的测试是不必要的。 @AlanObject 这段代码跳过了有趣的字符串出现在source开头的情况真的不是一个错误吗?

以上是关于为啥在 Javascript 中不鼓励在 while 语句中赋值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥现代 JavaScript 框架不鼓励与 DOM 直接交互

为啥 [1,2,3] 在 Javascript 中不等于自身? [复制]

为啥我的 html 请求在 javascript 中不起作用 [重复]

为啥我的 javascript 箭头函数在 Edge/IE 中不起作用?

为啥这个 Javascript 库在浏览器中可用,但在 PhoneGap 中不可用?

为啥这个 javascript 字符串代码在 Internet Explorer 7 中不起作用?