03月25, 2015

使用CoffeeScript时遇到的一些坑

在CoffeeScript刚推出的时候关注过,那时候编译出来的JavaScript代码调试起来是个问题。经过不断改进,编译出的JavaScript代码可读性已经不再是问题了。

CoffeeScript是一个CoffeeScript语言到JavaScript的Transcompiler。其语法受到Ruby,Python的印象。如果你曾经使用过Ruby、Python。那么上手CoffeeScript是分分钟的事情。 比如

  • Parallel Assignment(ES6中的Destructing,a, b = [1,2]
  • Postfix modifiers(放在表达式后面的exp if/unless/while)
  • Existential operator(array.include?)
  • Array comprehensions等。(x for x in array)
  • Fat Arraow (x = => {})
  • String interpolation("#{variable} world")

在使用过程中遇到了一些问题,总结如下:

无用的数组

和Ruby一样,函数总会返回最后一个表达式的结果。 所以下面的代码中的for循环会被以数组形式返回。为此,会生成一个临时变量result来保存循环中的最后一个表达式,即console.log(x)。而这个结果有时候是无用的。

a = [1,2,3];
dump = ->
  for x in a
    console.log(x);

被翻译成

var a, dump;
a = [1, 2, 3];
dump = function() {
  var i, len, results, x;
  results = [];
  for (i = 0, len = a.length; i < len; i++) {
    x = a[i];
    results.push(console.log(x));
  }
  return results;
};

避免生成‘无用数组’的的方法很简单,在最后加上一个return就可以了:

a = [1,2,3];
dump = ->
  for x in a
    console.log(x);
  return

被翻译成

var a, dump;
a = [1, 2, 3];
dump = function() {
  var i, len, x;
  for (i = 0, len = a.length; i < len; i++) {
    x = a[i];
    console.log(x);
  }
};

没有函数声明

CoffeeScript中无法使用函数声明,只有函数表达式,并且都是匿名的。 换句说法就是,CoffeeScript不允许函数提升(Hoisting),也不产生Named Function Expression。

函数声明和具名函数表达式在stack trace时有一定的帮助。 Issue#15Issue#1640都有对这个feature的请求。

CoffeeScript中唯一出现函数声明的地方就是类定义:

class Clazz
  fn: () -> alert(1)

被翻译成

var Clazz;

Clazz = (function() {
  function Clazz() {}
  Clazz.prototype.fn = function() {
    return alert(1);
  };
  return Clazz;

})();

非primitive的类属性

class Animal
  behaviors: ["eat","sleep","drink","run"]

cat = new Animal();
dog = new Animal();

cat.behaviors.push("miaow");

console.log cat.behaviors
# => ["eat", "sleep", "drink", "run", "miaow"]

console.log dog.behaviors
# => ["eat", "sleep", "drink", "run", "miaow"]

dog也有了miaow的行为。这是因为catdog实例的behaviors都指向同一个数组实例。解决的方法是在构造函数中设置behaviors属性的值:

class Animal
  constructor: ->
    @behaviors = ["eat","sleep","drink","run"]

cat = new Animal();
dog = new Animal();

cat.behaviors.push("miaow");

console.log cat.behaviors
# => ["eat", "sleep", "drink", "run", "miaow"]

console.log dog.behaviors
# => ["eat", "sleep", "drink", "run"]

函数调用的括号

CoffeeScript中的函数调用可以省略括号。但是有时候会导致意想不到的问题:

x = "hello"
console.log x +1

会被翻译成

var x;
x = "hello";
console.log(x(+1));

注意到了么,x +1中加好后面没有空格。所以x被当做函数调用,而+1被当做“正数1”了。

在CoffeeScript源码中使用JavaScript代码

有时候会遇到这样的问题:有一部分历史代码是用JavaScript写的,但是翻译成CoffeeScript代价又比较大。此时可以用backtick(`)在CoffeeScript中嵌入JavaScript代码:

f()

`function f() {`
console.log "hello world!"
`}`

被翻译成

f();
function f() {;
    console.log("hello world!");
};

这也算是在CoffeeScript中使用函数声明的一个Trick吧。当然不推荐这样做。

慎用Grunt编译CoffeeScript

最初项目是用Grunt进行构建。但在公司笔记本上,机械硬盘实在不给力,每次保存*.coffee后带来的编译都要花费1到2秒钟,这一点是无法忍受的。

切换到Gulp之后好了许多。配合ssd效果更好。 至于在ssd上使用Grunt是否这么慢没有进行测试。

总结

CoffeeScript只是JavaScript衣柜里的一件衣服。 衣柜里比较有名的还有:

随着Babel及其插件的不断完善、Reactjs的流行。CoffeeScript的吸引力变得越来越小。除非你实在不想打花括号和圆括号。

书籍推荐

本文链接:http://aztack.wang/post/pitfalls-of-using-coffeescript-in-projects.html

-- EOF--

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。