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

时间:2015-02-26 12:50:37

标签: javascript coffeescript

我正在玩咖啡剧本(我是这个"语言"的新手),并尝试了一个非常基本的例子:

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]);
}

为什么咖啡脚本编译成看起来不自然的方式?如果我在Javascript中编写完全相同的代码,我会遵循我所做的方法,但我可能错了,因为我做的方式(Javascript)不是最有效的或者#34;自然的&#34;而咖啡脚本方法是一个更好的解决方案。

2 个答案:

答案 0 :(得分:4)

基本上你期望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]);,编译器必须考虑该语句周围的所有。它会给编译器执行一项非常复杂的工作,并且不会真正提供任何好处。

答案 1 :(得分:1)

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

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

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

下一行是:

var element, i, len, x;

在使用var之前声明x = [1, 2, 3]; 被认为是一种良好做法(至少根据jslint)。

您对x的定义当然是:

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

现在循环:

len

首先定义x.length会阻止循环不断查找console.log。只有三个项目不是一个巨大的速度差异,而是一个10k的数组,那么......

element很明显,但定义了i变量,因为这是您在编写循环时指定的变量的名称。它使用i代替元素进行迭代,主要是因为使用{{1}}进行迭代是标准做法。

所以你看到一切都有正当的理由,虽然对某些品味有点冗长。