证明Crockford的说法

21

我已经阅读了Crockford的JavaScript: The Good Parts,并使用过他的验证器JSLint。有时我对他的建议背后的理由感到疑惑。以下是我想要证实其支持的示例列表。


  1. 为什么如果不包含"use strict";会导致JSLint发出错误信号?[参见此 SO thread]

  2. 为什么函数内的变量声明应该使用单个var?[参见此 SO thread]

  3. 为什么在function ()中间需要加入空格?

  4. 为什么不能使用continue

  5. ++--有什么问题吗?

  6. 为什么不能使用逗号运算符,(除了在for语句的初始化和递增部分)?[参见此博客文章]

  7. 为什么每个语句都应该以;结尾?[参见此博客文章]


3
这可能应该放在http://programmers.stackexchange.com/上。 - OverZealous
4
我尊重 Crockford 先生,并且在 JavaScript 实践方面我同意他的很多观点(如果不是全部),但我认为在某些情况下,他的解释属于“因为我这么说”的范畴。 - nnnnnn
1
@c4757p:是的,JavaScript确实有一些优秀的部分。 - icktoofay
1
这是一些好问题,我投票支持重新开放。我很想听听那些断言背后的推理。 - Darko
1
我重新打开了这个问题。我希望在共同撰写的答案中简要回答这7个问题。 - Šime Vidas
显示剩余6条评论
2个回答

31

大部分JSLint的行为都在JSLint指令页面中解释了。我从现在开始将其称为JIP。

没有被JIP解释的行为大多在Crockford的JavaScript编程语言代码约定中得到了解释。我从现在开始将其称为CCJPL。

按照列表进行:

1)JSLint为什么会在不包含"use strict";时发出错误信号?

因为严格模式可以执行许多良好的实践;而JSLint也是关于执行良好实践的,所以这是它的行为。查看"use strict"启用的内容有助于澄清此问题。我只是按照Resig的博客文章中的列表进行操作,因此应该给予相应的功劳。我并未对它所做的每个功能进行评论,因为我不确定Crockford每个功能的确切原因。

  1. 在赋值前必须使用var声明变量:这可以防止隐含的全局变量出现。Crockford认为全局变量是邪恶的,这有助于消除你没有显式设置的全局变量。
  2. 它限制了eval,这被JIP视为邪恶。
  3. 它禁用了with语句。这被Crockford认为是有害的

我只省略了严格模式的两个功能:修改delete语句的行为和覆盖arguments变量。我不知道Crockford是否同意或反对这两个事情,但考虑到他帮助创建了ECMAScript 5标准,我倾向于认同。

2) 为什么应该使用单个var在函数内部进行变量声明?

首先,任何未经var声明而定义的变量都是隐含全局变量,这根据JIP可以“掩盖拼写错误的名称和其他问题”。因此,这是需要var的原因。

关于要求只有一个var,JavaScript引擎会自动将所有变量声明提升到它们作用域的顶部,所以我的猜测是Crockford希望开发人员能对这种行为有敏锐的认识。除此之外,这可能只是一种风格上的选择。你更喜欢下面两段代码中的哪一段:

var a, b, c;

或者

var a; var b; var c;

我的猜测是Crockford认为多个var很丑。在这一点上我同意他的看法。

具有讽刺意味的是,根据CCJPL,他实际上建议将所有变量声明放在不同的行上,并附带注释。在这方面,JSLint与他的看法不同!

3)为什么需要在function ()之间加上空格?

这仅适用于匿名函数。根据CCJPL,原因是:

如果函数文字是匿名的,则应该在单词“function”和左括号“(”之间有一个空格。如果省略了空格,则可能会出现函数名称为function的错误读取。

当我深入学习JavaScript时,我虔诚地开始遵循Crockford的惯例……但是我的逻辑部分说,如果您的代码将函数命名为function,那么它可能急需重新编写。(更不用说在JavaScript中,这是一个SyntaxError,因为function是一个关键字。)

4)为什么不能使用continue

引用CCJPL的话:

它往往会混淆函数的控制流程。 并不是一个详细的回答,但我在互联网上看到过将其与goto进行比较的争论。您可以理解为这样。 5)++和--有什么问题? 如JIP所述: 增量(++)和减量(- -)运算符已知通过鼓励过度狡猾而导致糟糕的代码。除了错误的体系结构之外,它们仅次于启用病毒和其他安全威胁。此外,预增量/后增量混淆可能会产生极难诊断的差一错误。 6)为什么我们不能使用逗号运算符“,”(除了在for语句的初始化和增量部分中)? JIP的另一个详细解释: 逗号运算符可能会导致过度狡猾的表达式。它还可以掩盖一些编程错误。 这真是太遗憾了,因为正如您链接的博客文章所示,逗号运算符确实可以在某些情况下增加代码的清晰度。 您会注意到Crockford的许多惯例都有一个共同的基本主题:易于理解、可读的代码。这只是其中的另一个例子。许多惯例都集中于拒绝访问“狡猾”的表达式。
但我认为描述这些表达式的真正词语应该是“简洁”,这并不一定是坏事,但如果使用不当就可能会有害。Crockford的规则有时会尝试通过删除一些工具来消除错误使用的简洁性,即使在某些情况下它们有合法的用途。
7)为什么每个语句都应该以“;”结尾?
再次引用JIP的话:
JavaScript使用类似C的语法,需要使用分号来分隔某些语句。 JavaScript尝试使用分号插入机制使这些分号变成可选项。这很危险,因为它可能掩盖错误。
像C一样,JavaScript有++和--和(运算符可以是前缀或后缀。分号进行了区分。
通常,我发现Crockford解释中“可能掩盖错误”的部分相当广泛和模糊,但分号插入确实是一个我完全同意的领域。在每个语句后使用显式分号比冒险缩小危险的分号插入范围更容易。
以下是维基百科JavaScript Syntax页面上出现分号插入错误的经典示例:
return
a + b;

// Returns undefined. Treated as:
//   return;
//   a + b;

如果您使用像这篇博客文章中的代码一样的对象字面量(一种相当常见的大括号样式),那么它也可能会使您困扰:

function shoeFactory() {
    return // <--- semicolon inserted here
    {
        shoeSize: 48
    };
}

var shoe = shoeFactory();
console.log(shoe); // undefined

来自上述维基百科文章的另一个示例:

a = b + c
(d + e).foo()

// Treated as:
//   a = b + c(d + e).foo();

我从来没有因为要添加显式分号而感到困扰。在编写几乎任何代码时,它们对我来说都非常自然。事实上,即使在Python中不需要分号,我经常发现自己还是会加上分号。

在这种情况下,我非常赞同Crockford的观点。我认为一次关于分号插入的调试会议可能会让大多数人相信,将它们放在代码中会更容易。


1
我强烈反对第4和第5点。continue并不会使任何东西变得模糊。它也不是特定于JavaScript的,所以每个人都应该熟悉它。这通常是跳过当前迭代其余部分的好方法。而++/--“鼓励过度巧妙”?他还继续谈论启用病毒?这是一个简单的运算符。同样,并不是JS特定的。如果有人容易忘记++x返回新值和x++返回旧值,那就看看+/-符号在哪里。但他有很好的建议。 - basic6

12

你说得没错,Crockford很少证明他的实践方法。

例如,在我看来,在作用域中定义var不一定要放在顶部,只要你知道它们会被JavaScript提升到作用域的顶部。此外,我认为这实际上是有益的,因为你可以轻松地挑选出一个变量的初始值,只需要查找 var 即可。

如果你知道如何使用,我还认为case语句穿透非常有益,特别是编写像非常复杂的状态机之类的东西时。

iffor后面只有一个语句而没有花括号也没问题。几十年来,人们一直以这种方式编写C代码。

如果你知道什么构成了JavaScript语句的结尾,那么分号也是不必要的。分号插入不是随机的,与普遍的看法相反,它并不是无法理解的。

Crockford因垃圾回收而闻名......这太糟糕了。非常强大(而且快速)的词法分析器可以使用regex作为词元/规则/语法/等编写。

在大多数情况下,Crockford知道他在谈论什么,但不要将他的个人风格视为教义。在我的看法中,一些最好的JavaScript编码者直接反对Crockford在他们的代码方面的风格。

编辑:我不明白为什么这个问题被关闭。它可以客观地进行讨论,并值得讨论。


1
要明确一点,我并不是指程序员本身在口头上反对Crockford,我只是指他们的特定风格是这样的。但是如果你想看到一个主张不使用分号的人,例如,你可以看看Isaac Schlueter的博客:http://blog.izs.me/。Isaac是Node.js的开发人员之一。 - chjj
6
完全同意。Crockford的问题在于,他说出的一些东西,无论是基于经验还是个人偏好,都表现得十分有权威性,并且攻击任何持不同意见者的态度也极其强硬。这位大佬是一名了不起的开发者,同时也是现代JavaScript之父,但对于那些在风格方面与他意见相左的人而言,我们不得不不断地为自己的观点进行辩解,因为一些人视他的话为圣旨。 - Matt Briggs
1
@MattBriggs,我也同意! - Alnitak
1
但是在有人编写并维护像JSLint一样方便且具有坚实最佳实践的工具之前,采纳JSLint的建议(或者JSHint的建议,使用您计算机团队的标准选项集)具有实际优势。创建高质量代码的文化不是关于承认每个人对最佳定义的主观看法。质量的提高来自于企业(至少在两个意义上)约定俗成的标准化。无论你怎么说Crockford,至少这是一种精神。而且,客观地说,这些建议并没有功能上的错误。 - ruffin
1
@ruffin 相比之下,PEP8 对 Python 做出了巨大的贡献,愿意遵循代码一致性的意愿令人惊叹。在这里,一致性才是关键,而不是谁是正确的,更有价值的是在更多情况下保持一致性,但这并不是必须的。这并不是说“永远”不要打破规则,而是说,如果你要与更广泛的受众分享代码,请尽量遵循某些更高的标准。不要过度思考,我只是在谈论针对大众的代码约定,个人主义也有其存在的地方。 - Derek Litz
显示剩余4条评论

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