十分钟教会你coffeeScript

1,217 阅读7分钟

1、背景

接手了一个极其老旧(jsp,xml,jq),庞大(一个js文件8000行)中突然用到coffeeScript,一下给我干懵了,身为一个只知道cv,浑身上下最亮的闪光点只有会百度的专业拧螺丝工具人,不得不说十分的痛苦,明明差不多凉了(2014年就停止更新了)......一边看文档一边写coffee文件,伤心的我眼泪掉下来,为了不被优化,开始学一下吧(注:因为是老项目,所以不会用到ES6语法,coffee也是被ES6干掉的....),在此希望能抛砖引玉,有不对的地方烦请各位大佬指出

2、安装

这个没啥好说的,直接在项目里npm install coffee-script 或者引入包文件

3、语法

首先, 一些基础, CoffeeScript 使用显式的空白来区分代码块. 你不需要使用分号 ; 来关闭表达式, 在一行的结尾换行就可以了(尽管分号依然可以用来把多行的表达式简写到一行里). 不需要再用花括号来 { } 包裹代码快, 在 函数if 表达式switch, 和 try/catch 当中使用缩进.

传入参数的时候, 你不需要再使用圆括号来表明函数被执行. 隐式的函数调用的作用范围一直到行尾或者一个块级表达式.
console.log sys.inspect object → console.log(sys.inspect(object));

3.1、函数

这个可以说是用的最多的了,代码中从上到下到处都充斥着这玩意 在coffeeScript中,函数声明直接变成了一个箭头,我称之为瘦箭头 ->,反正返回最后一句,不得不说,代码确实简洁了点,比如说一段这样的js代码

var square = function(x) { return x * x; }; 
var cube = function(x) { return square(x) * x; };
var fill = function(container, liquid) { 
        if (liquid == null) { liquid = "coffee"; } 
        return "Filling the " + container + " with " + liquid + "..."; 
    };

用coffeeScript写的话,如下

square = (x) -> x * x 
cube = (x) -> square(x) * x
fill = (container, liquid = "coffee") -> 
    "Filling the #{container} with #{liquid}..."

用官网文档的话说就是
1、函数通过一组可选的圆括号包裹的参数, 一个箭头, 一个函数体来定义. 一个空的函数
2、一些函数函数参数会有默认值, 当传入的参数的不存在 (null 或者 undefined) 时会被使用.

翻译成人话就是:用瘦箭头->代替了function(){},而且参数里面还可以添加默认参数

3.2 声明变量

这个简单,直接写变量名就行了,类似Python,如下

outer = 1 
changeNumbers = -> 
    inner = -1 
    outer = 10 
inner = changeNumbers()
console.log outer,inner
// 输出结果:10 10
# 

3.2 对象和数组

数组的写法跟 JavaScript 中的写法差不多.对象的话,如果单个属性被写在自己的一行里, 那么逗号是可以省略的. 和 YAML 类似, 对象可以用缩进替代花括号来声明.

song = ["do", "re", "mi", "fa", "so"] 
singers = {Jagger: "Rock", Elvis: "Roll"} 
bitlist = [ 
            1, 0, 1 
            0, 0, 1 
            1, 1, 0 
          ] 
kids = brother: 
         name: "Max" 
         age: 11 
       sister: 
         name: "Ida" 
         age: 9
# 

相当于

var bitlist, kids, singers, song; 
song = ["do", "re", "mi", "fa", "so"]; 
singers = { 
    Jagger: "Rock", 
    Elvis: "Roll" 
}; 
bitlist = [1, 0, 1, 0, 0, 1, 1, 1, 0]; 
kids = { 
    brother:{ 
        name: "Max", 
        age: 11 
    }, 
    sister: { 
        name: "Ida", 
        age: 9 
    } 
   };

3.3 if, else, unless 和条件赋值

在coffee中,if/else 表达式可以不用圆括号和花括号就写出来,直接换行缩进就行了,还是照例,不用小括号,另外还有一个顺手的后缀形式, 在行尾使用 if or unless,而且CoffeeScript 里不存在直白的三元表达式. — 你只要在一行内使用普通的 if 语句来达到三元表达式的效果

mood = greatlyImproved if singing //判断singing是否存在,如果存在则赋值greatlyImproved给mood
if happy and knowsIt //这里and相当于&&,去掉了小括号
    clapsHands() 
    chaChaCha() 
else 
    showIt() 
date = if friday then sue else jill //这里相当于friday ? sue : jill

3.4 循环和推导式

官方文档原话是:你可以使用CoffeeScript将大多数的循环写成基于数组、对象或范围的推导式(comprehensions)。 推导式替代(编译为)for循环,并且可以使用可选的子句和数组索引值。 不同于for循环,数组的推导式是表达式,可以被返回和赋值。

翻译一下:就是coffee的循环是表达式,可以返回和赋值,下面上代码

# 吃午饭. 
eat food for food in ['toast', 'cheese', 'wine'] 
//这里分成两部分,for前面的,执行一个eat方法,
//后面相当于在for循环中声明了一个food的变量,然后依次循环值,赋予food,再把food当成参数传入eat方法
//注意,第一个food就是第一个food,如果改成 eat a for food in ['toast','cheese','wine']则会报错未定义变量a

# 精致的五道菜. 
courses = ['greens', 'caviar', 'truffles', 'roast', 'cake'] 
menu i + 1, dish for dish, i in courses 
//这里同上,就多了个索引i而已

# 注重健康的一餐. 
foods = ['broccoli', 'spinach', 'chocolate'] 
eat food for food in foods when food isnt 'chocolate'

//这里和上面两位稍微有一丢丢区别,when food isnt 'chocolate'过滤了一下,就是当food不等于chocolate的时候才赋值

#倒计时
countdown = (num for num in [10..1])
//这里唯一的区别就是后面跟了个[10..1],代表一个从10到1的循环,然后就简单了,10到1递减呗,有一个小括号,表示返回数组给countdown,countdown的值是[10,9,8,7,6,5,4,3,2,1]

#倒计时ver2.0版本
countdown = num for num in [10..1]
//没有小括号,赋值给countdown,每次循环的值,countdown的值是1,不懂为啥是1的小伙伴移步闭包,执行上下文,作用域链那块

#遍历对象
yearsOld = max: 10, ida: 9, tim: 11 
ages = for child, age of yearsOld 
    "#{child} is #{age}"
//用of遍历对象,跟js差不多,换行缩进的为for中的操作逻辑并push进一个数组,最终返回一个长度为3滴数组给ages
// ['max is 10', 'ida is 9', 'tim is 11']

基本循环掌握这些就够了,剩下的while啦~while not啦~by啦~等等有兴趣的可以到官网看看,毕竟这种老技术,其实没有太大的学习价值了

3.4 数组的切片和用 range 进行拼接

Range 也可以被用来展开数组的切片. 通过两个点号的写法 (3..6), range 会包含最后一个数据 (3, 4, 5, 6); 通过三个点号的写法 (3...6), range 不会包含最后一个数据 (3, 4, 5). 切片的索引位置存在不错的默认值. 前面的索引位置省略的话, 默认会是 0, 后面的索引位置被省略的话, 默认值是数组的大小.

翻译:
感觉这里讲的还挺明白了,基本就是
两个点:包前又包后
三个点:包前不包后

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] 

start = numbers[0..2] 
//start = numbers.slice(0, 3);

middle = numbers[3...-2] 
//middle = numbers.slice(3, -2);

end = numbers[-2..] 
//end = numbers.slice(-2);

copy = numbers[..]
//copy = numbers.slice(0);

3.5 操作符和 aliase和 模板字符串

逻辑操作符合映射关系,这个没得说,背一下,几分钟的事

CoffeeScriptJavaScript
is===
isnt!==
not!
and&&
or``
trueyesontrue
falsenoofffalse
@thisthis
ofin
inno JS equivalent
a ** bMath.pow(a, b)
a // bMath.floor(a / b)
a %% b(a % b + b) % b

字符串替换, 块级的字符串, 块级的注释,相当于ES6中的模板字符串,${}变成#{},这里也不必过多赘述

author = "Wittgenstein" 
quote = "A picture is a fact. -- #{ author }" 
sentence = "#{ 22 / 7 } is a decent approximation of π"

3.6 箭头函数,解构,class,继承,super

官方文档中称之为函数绑定.....emmmm,就这个东西 =>,箭头函数和解构,class,继承,super跟ES6十分类似,也不过多展开

//箭头函数
Account = (customer, cart) -> 
    @customer = customer 
    @cart = cart 
    $('.shopping_cart').bind 'click', (event) => 
        @customer.purchase @cart
        
// 解构
theBait = 1000 theSwitch = 0 
[theBait, theSwitch] = [theSwitch, theBait]

//class,继承,super
class Animal 
    constructor: (@name) -> 
    
    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()

3.7 Switch/When/Else

coffee中稍微修改了一下,语法没变,替换了一下关键字,个人感觉还比较符合逻辑,看一遍就会~
JavaScript 里的 Switch 语句中. 你需要在每个 case 写 break 防止自动进入默认的 case. CoffeeScript 会阻止掉意外的 fall-through. 而且 switch 编译的结果会是可以带 return, 可以被用于赋值的表达式. 格式这样写: switch 判断条件, when 然后子句, else 然后默认的 case.

switch day 
when "Mon" then go work 
when "Tue" then go relax 
when "Thu" then go iceFishing 
when "Fri", "Sat" 
    if day is bingoDay 
        go bingo 
        go dancing 
when "Sun" then go church 
else go work

照例,还是可以有返回值
score = 76 
grade = switch 
    when score < 60 then 'F' 
    when score < 70 then 'D' 
    when score < 80 then 'C' 
    when score < 90 then 'B' 
    else 'A' 
# grade == 'C'

3.8 Try/Catch/Finally

Try/catch 语句基本上 JavaScript 的一样,就缩进一下,括号去掉

try 
    allHellBreaksLoose() 
    catsAndDogsLivingTogether() 
catch 
    error print error 
finally 
    cleanUp()

最后附上官方文档链接,方便跳转coffee-script.org/#top