为什么 CoffeeScript 编译成单一的 JavaScript?

Why does CoffeeScript compile to unidiomatic JavaScript?

我正在尝试使用 Coffee 脚本(我是新手 "language")并尝试了一个非常基本的示例:

x = [1, 2, 3] 
for element in x 
  console.log(element)

这就是它所说的,对于它输出到控制台的每个元素。只有当我在 Javascript 它编译成的时候花了很多时间,我才不明白他们为什么这样做:

(function() {
  var element, i, len, x;

  x = [1, 2, 3];

  for (i = 0, len = x.length; i < len; i++) {
    element = x[i];
    console.log(x[i]);
  }

}).call(this);

这样做会不会更好更自然:

for(i = 0; i < x.length; i++)
{
   console.log(x[i]);
}

为什么 coffee 脚本编译成这样看起来不自然的方式?如果我在 Javascript 中编写完全相同的代码,我会遵循我所做的方法,但我可能错了,因为我这样做的方式 (Javascript) 不是最有效的或 "natural" 而coffee script方法是更好的解决方案。

基本上,您期望 CoffeeScript 编译器执行非常复杂的杂技操作。

考虑这个 CoffeeScript:

for a in x
  console.log(a)
  for b in a
    console.log(b)
    callFunc(b)

这会转换为:

for (i = 0, len = x.length; i < len; i++) {
  a = x[i];
  console.log(a);
  for (j = 0, len1 = a.length; j < len1; j++) {
    b = a[j];
    console.log(b);
    callFunc(b);
  }
}

很容易看出原始 CoffeeScript 如何对应于结果 JavaScript:

for [variable] in [array]
    [body]

变成

for (var [letter] = 0, len = [array].length, [letter] < len; [letter]++) {
    [variable] = [array][[letter]]
    [body converted to JavaScript]
}

所以当把一个for理解翻译成JavaScript时,(几乎)所有编译器需要担心的就是这个简单的对应关系,然后它可以向内工作并将主体作为一个单独的部分处理并独立运作。

此外,len = [array].length 缓存是为了提高性能。这看起来并不令人愉快,而且大多数人不会以这种方式编写代码,但是生成漂亮的 JavaScript 并不是 CoffeeScript 的主要目标。它生成高效代码的前提是人们通常不会阅读生成的 JavaScript.

您的建议是将原始代码转换为:

for (i = 0; i < x.length; i++) {
  console.log(x[i]);
  for (j = 0; j < x[i].length; j++) {
    console.log(x[i][j]);
    callFunc(x[i][j]);
  }
}

为了将 console.log(b) 转换为 console.log(b);,编译器不需要知道 任何 围绕该语句的代码。为了将 console.log(b) 转换为 console.log(x[i][j]);,编译器必须考虑该语句周围的 一切 。它会给编译器带来极其复杂的工作,并且不会真正提供任何好处。

首先,CoffeeScript 默认在闭包中构建所有内容,即这些行:

  (function() {
  }).call(this);

闭包防止变量从脚本中泄漏出来。如果您想将脚本导出为全局脚本,则需要将其显式附加到全局,例如 window.

下一行是:

var element, i, len, x;

在使用之前声明您的 var 被认为是好的做法(至少根据 jslint)。

你对x的定义当然是:

x = [1, 2, 3];

现在循环:

for (i = 0, len = x.length; i < len; i++) {
  element = x[i];
  console.log(x[i]);
}

首先定义 len 可以防止循环不断地查找 x.length 是什么。只有三个项目的速度差异不大,但是一个 10k 的数组,嗯...

console.log 很明显,但 element 变量已定义,因为这是您在编写循环时指定的变量名称。它使用 i 而不是元素进行迭代,主要是因为使用 i 进行迭代是标准做法。

所以你看到一切都有合理的理由,尽管对某些人来说有点冗长。