参考资料:
CoffeeScript 笔记
coffeescript中文
coffeescript英文
CoffeeScript 是一门编译到 JavaScript 的小巧语言.
CoffeeScript 的指导原则是: “她仅仅是 JavaScript”. 代码一一对应地编译到 JS, 不会在编译过程中进行解释。 已有的 JavaScript 类库可以无缝地和 CoffeeScript 搭配使用, 反之亦然。 编译后的代码是可读的, 且经过美化, 能在所有 JavaScript 环境中运行, 并且应该和对应手写的 JavaScript 一样快或者更快.
通过npm 全局安装coffeescript
npm install coffeescript -g
「语法糖」可以理解为让代码的读写更简单。
概例
# 赋值:
number = 42
opposite = true
# 条件:
number = -42 if opposite
# 函数:
square = (x) -> x * x
# 数组:
list = [1, 2, 3, 4, 5]
# 对象:
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x
# Splats:
race = (winner, runners...) ->
print winner, runners
# 存在性:
alert "I knew it!" if elvis?
# 数组 推导(comprehensions):
cubes = (math.cube num for num in list)
对应的js:
var cubes, list, math, num, number, opposite, race, square,
__slice = [].slice;
number = 42;
opposite = true;
if (opposite) {
number = -42;
}
square = function(x) {
return x * x;
};
list = [1, 2, 3, 4, 5];
math = {
root: Math.sqrt,
square: square,
cube: function(x) {
return x * square(x);
}
};
race = function() {
var runners, winner;
winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
return print(winner, runners);
};
if (typeof elvis !== "undefined" && elvis !== null) {
alert("I knew it!");
}
cubes = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = list.length; _i < _len; _i++) {
num = list[_i];
_results.push(math.cube(num));
}
return _results;
})();
CoffeeScript 移除了所有的大括号和分号。
CoffeeScript 使用显式的空白来区分代码块. 你不需要使用分号 ;
来关闭表达式, 在一行的结尾换行就可以了(尽管分号依然可以用来把多行的表达式简写到一行里). 不需要再用花括号来 { }
包裹代码快, 在 函数, if 表达式, switch, 和 try/catch 当中使用缩进.
CoffeeScript 把编译生成的 JS 封装在一个匿名函数中:
(function(){
// 这里是编译生成的代码
}).call(this);
这样就巧妙避免了全局作用域的污染。同时,CoffeeScript 始终在编译生成的 JS 代码中用 var
声明变量。
CoffeeScript 中有个操作符 ?
,用于检测变量是否存在。
console.log html if html?
这句 CoffeeScript 编译过来为(去掉了匿名封装函数,为了方便,之后的编译后代码都去掉)
if (typeof html !== "undefined" && html !== null) {
console.log(html);
}
可见,?
会先检测变量有没有定义,如果定义了再检测是否为 null。
CoffeeScript 中去掉了 function
关键字。用 () ->
定义一个函数。空函数看起来像这样:->
。括号内为参数,可以为参数设置默认值。当传入的参数不存在 (null 或者 undefined) 时,默认值会被使用.如:
myFunction = (a, b = 2) ->
a + b
编译为:
var myFunction;
myFunction = function(a, b) {
if (b == null) {
b = 2;
}
return a + b;
};
调用函数的时候,还可以不用括号。如:
myFunction 3, 5
有一点需要注意一下,CoffeeScript 会在编译后的 JS 代码中自动为最后一行添加 return
关键字。所以不论函数的最后一行是什么,都会成为返回值。如果你不想让最后一行成为返回值,就需要另起一行自己加上 return
。
splat 操作符非常强大。在你的函数需要接受可变数量的参数时就需要它了。书上的例子:
splatter = (etc...) ->
console.log "Length: #{etc.length}, Values: #{etc.join(', ')}"
// CoffeeScript 中字符串插值用 #{}
splatter()
splatter("a", "b", "c")
// 输出
Length: 0, Values:
Length: 3, Values: a, b, c
就在某个参数后面加上...
,就使传入的参数自动转化为一个数组。splat 操作符可以出现在参数列表的任意位置,但是参数列表中只能有一个 splat 操作符。
一般定义数组是这样:
myArray = ["a", "b", "c"]
在 CoffeeScript 里你还可以这样:
myArray = [
"a"
"b"
"c"
]
如果单个属性被写在自己的一行里, 那么逗号是可以省略的.
Array.prototype.indexOf
,在 CoffeeScript 中只需要用 in
:console.log "d was not be found" unless "d" in myArray
// 输出
d was not be found
eat food for food in ['toast', 'cheese', 'wine']
编译成
var food, _i, _len, _ref;
_ref = ['toast', 'cheese', 'wine'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
food = _ref[_i];
eat(food);
}
在推导式中使用by子句,可以实现以固定跨度迭代范围值: evens = (x for x in [0..10] by 2)
推导式也可以用于迭代对象中的key和value。在推导式中使用of
来取出对象中的属性,而不是数组中的值。
yearsOld = max: 10, ida: 9, tim: 11
ages = for child, age of yearsOld
"#{child} is #{age}"
x = "X"
y = "Y"
[x, y] = [y, x]
交换 x、y 的值就这么简单!
myArray = ["A", "B", "C", "D"]
[start, middle..., end] = myArray // 可配合 splat 操作符使用
console.log "start: #{start}"
console.log "middle: #{middle}"
console.log "end: #{end}"
// 输出
start: A
middle: B,C
end: D
区间(range)能让定义包含两个数字之间所有数字的数组变得很容易。
myRange = [1..10]
console.log myRange
// 输出 [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
如果不想包括结束数值,可以用 ...
代替 ..
。
myRange = [10...1]
console.log myRange
// 输出 [ 10, 9, 8, 7, 6, 5, 4, 3, 2 ]
常见的数组操作,都可以通过区间完成:
myArray = [1..10]
// 分割数组
// 前面的索引位置省略的话, 默认会是 0, 后面的索引位置被省略的话, 默认值是数组的大小.索引位置值可以是负数,表示逆序。
part = myArray[0..2]
console.log part
// 输出 [ 1, 2, 3 ]
// 替换数组值
myArray = [1..10]
// 前面的值startIndex是索引位置,后面的值endIndex等于endIndex-startIndex+1
myArray[4..7] = ["a", "b", "c", "d"]
console.log myArray
// 输出 [ 1, 2, 3, 4, 'a', 'b', 'c', 'd', 9, 10 ]
// 插入值
myArray = [1..10]
// 如果startIndex>endIndex,则为插入操作,反之则为替换
myArray[4..-1] = ["a", "b", "c", "d"]
console.log myArray
// 输出 [ 1, 2, 3, 4, 'a', 'b', 'c', 'd', 5, 6, 7, 8, 9, 10 ]
CoffeeScript 会把 ==
编译为 ===
, 把 !=
变异为 !==
.
until
关键字等同于while not
, loop
关键字 等同于while true
。在 while, if/else, switch/when 的语句当中, then
可以被用来分隔判断条件跟表达式, 这样就不用强制写换行或者分号了.
CoffeeScript 引入了很多别名来代替一些关键字:
别名 | 对应关键字 |
---|---|
is | === |
isnt | !== |
not | ! |
and | && |
or | || |
true, yes, on | true |
false, no, off | false |
@, this | this |
of (探测 JavaScript 对象的属性是否存在) | in |
in (判断数据在数组中是否出现) | no JS equivalent |
a ** b (乘方) | Math.pow(a, b) |
a // b (整除) | Math.floor(a / b) |
a %% b (模运算) | (a % b + b) % b |
一例胜千言:
class Animal
constructor: (@name) ->
// 用 @property: value 赋值静态的属性
@type: 'animal'
move: (meters) ->
alert @name + " moved #{meters}m."
class Snake extends Animal
move: ->
alert "Slithering..."
super 5
class Horse extends Animal
move: ->
alert "Galloping..."
super 45
sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"
sam.move()
tom.move()
new
的时候调用,可以重写它。::
就和 JS 里的 prototype
一样