我正在玩咖啡剧本(我是这个"语言"的新手),并尝试了一个非常基本的例子:
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;而咖啡脚本方法是一个更好的解决方案。
答案 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}}进行迭代是标准做法。
所以你看到一切都有正当的理由,虽然对某些品味有点冗长。