前言
- 本文适合没接触过CoffeeScript的人。
- 如果你想了解下CoffeeScript,可以看这篇笔记。
- 如果你想学习CoffeeScript,或者找CoffeeScript入门资料,请直接跳到最后看 books 部分。
安装coffee-script
sudo npm install -g coffee-script
编辑器
没找到好用的IDE。
vim可以安装vim-coffee-script。
使用Vundle安装,添加以下这行到vimrc。(需要你以前配置好了Vundle。)
Bundle 'kchmck/vim-coffee-script'
启动vim然后运行 :BundleInstall
。
编译
coffee -cwmo js coffee
会监视改动,同时会生成 source map,记录CoffeeScript和JavaScript的对应关系。
或者使用Harp.js之类的服务器,可以直接放.coffee
。
rack-coffee
和CoffeeCup分别为知名的Ruby和Python框架提供 CoffeeScript中间件,让编译透明化。
Node.js中也可以使用coffee
命令直接运行.coffee
文件。
浏览器中也可以加载CoffeeScript编译器后直接写CoffeeScript:
<script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js' type='text/javascript'></script>
<script type='text/coffeescript'>
# CoffeeScript
</script>
当然这会损失一些性能。
可以使用js2coffee将JavaScript转为CoffeeScript。
调试
通过打印来调试,例如:
window.debugMode = document.location.hash.match(/debug/) and console?
console.log 'debug mode output' if debugMode
这样只有当地址栏包含#debug
,且console开启时才会输出调试信息。
选择Chrome Developer Tools调试的话,记得在设置中开启source map支持。
选择Firefox的话,记得选择23或更新的版本。
基本语法
- 基于缩进的语法。
- 数组和哈希表,每项另起一行的话,可以省略逗号。
- 单引号下
\n
、\t
会被解析。单引号和双引号性能差别不大,而且CoffeeScript一般编译后使用,所以运行时单引号并没有性能优势,考虑到以后可能会使用插值字符串,所以尽可能使用双引号是个好主意。 ###
多行注释,"""
、'''
多行字符串、///
多行正则。- 字符串插值
#{expression}
==
和is
都会被编译为 JavaScript 中的===
,避免JavaScript中==
那样宽松的、强制类型转化的等于检查造成的问题。CoffeeScript社区有讨论过将==
和!=
移除,强制使用is
和isnt
,以免JavaScript用家混淆。Anyway,尽量使用is
和isnt
是个好主意。- Hash table中如果键值不奇怪的话,可以省略引号,相应地如果要用变量作键值,只能写成
table[key] = value
了。 loop
是while true
的简写形式,但是只能用于前缀。- 参数列
...
可以用来分割数组,用于函数定义时可以吸收所有多余的参数。 a?.b
吸收操作符- 闭区间
..
排外区间...
- Python里的
x, y = y, x
,CoffeeScript下写作[x, y] = [y, x]
。 for
语句后面用of
遍历哈希表,用in
遍历数组,后面可以跟when
。和python一样,for
循环不生成闭包。in
后可跟by
,指定步进。- 同样,列表解析也不生成闭包,和Python 2一样,和Python 3不同。
while
和until
后置的情况下如果起始条件不满足的话代码一次都不会被执行。- 条件表达式:
condition ? result : else-result
if
和unless
,is
和isnt
,使用起来很方便,很多时候都不需要写not
。如果你真打算用not
,要留神运算符优先级。a isnt 'a'
和a is not 'a'
是不一样的,后者中先运算not 'a'
,值为false
,整个语句等价于a is false
。还有,三元操作符不支持,a? b : c
中,b : c
会被解析为键值对。- 注意,数组即使元素相同也不相等,判断数组是否内容相同需使用
arrayEqual = (a, b) -> a.length is b.length and a.every (elem, i) -> elem is b[i]
。函数、对象同理。 yes
和on
是true
的别名,no
和off
是false
的别名。?
仅仅在null
和undefined
时返回false
,而if
在空字符串和数字0的情况下也返回false
。- 使用模块:
User = require("models/user")
- 如果希望能被其他模块访问:
module.exports.f = (x) -> x
- 除了
return
、continue
、break
外,一切皆为值。
注意,NaN是一个奇葩,typeof NaN
返回number
,但是NaN is NaN
返回false。IEEE754定义的,算一个tradeoff的设计。把这个放进语言算是JavaScript的坑,然后CoffeeScript没填上。
函数
do
可以形成闭包,和lua
一样。(事实上,do
类似lisp中的let
。)- 隐式返回最后一个表达式的值
- 函数调用省略括号
- 用
arguments
数组访问传递给函数的所有对象(低可读性)
@name
为this.name
的简写,this
表示上下文环境。相应的,有new
关键字,apply
和call
方法。
- 函数调用前若有
new
关键字,会把函数作为构造函数创建一个新对象,上下位即为该新对象。 - 使用
call
或者apply
调用函数时,给定的第一个参数为上下文。 - 函数作为对象的属性
obj.func
来调用时,该对象为上下文。 - 若不满足以上条件,则上下文为全局。
由于上下文在调用时绑定,因此可能会出现预料之外的情况。将->
改为=>
,可以确保this
的意义与函数定义所在位置的this
一样。
属性参数可以缩短代码,如
setName = (name) -> @name = name
可以简写为
setName = (@name) ->
使用表达式作默认参数,则表达式将在函数被调用的上下文中执行。
函数调用可以写成do f
。例如do (x) -> ...
是((x) -> ...)(x)
的简写,这个例子是node.js循环中捕获变量的常用写法。
类
- CoffeeScript会自动将整个类声明封装在
do ->
中,自己手工封装会报错。 @var: value
表明这个元素属于constructor(class)而不是prototype(instance)。这与@var = value
或this.var: value
不同。- 构造函数和其他函数不同,在没有显式
return
的前提下,返回的是构造对象。
try...catch
try
odd 5.1
catch e
console.log e
模块
root属性需要以root.attr
的形式明确定义。
root = global ? window
确保同时兼容Node和浏览器环境。
一个文件的exports
对象会在另一个文件require调用该文件时返回。
Books
- The Little Book on CoffeeScript 简明的介绍,四平八稳。
- Smooth CoffeeScript Eloquent JavaScript的CoffeeScript fork,适合没学过编程的人。否则会觉得节奏太迟缓了,这种情况下可以跳到最后看Language Reference。
- CoffeeScript 介绍CoffeeScript,感觉比The Little Book要好,然而不是免费的。我读的是同事买的寸志翻译的中文版。
- CoffeeScript Ristretto 理解 CoffeeScript 原理。强烈推荐!
私人建议:
如果只打算读1本,那推荐4,这本书不读真是太可惜了。
如果没学过编程,推荐先读2,然后再读4。
如果打算快速上手,那么可以读2——如果不打算花钱买,又借不到,读1也可。以后有空再读4。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。