官网文档传送门

CoffeeScript简介

CoffeeScript 是一门编译到 JavaScript 的小巧语言. 在 Java 般笨拙的外表下, JavaScript 其实有着一颗华丽的心脏. CoffeeScript 尝试用简洁的方式展示 JavaScript 优秀的部分.

CoffeeScript 的指导原则是: “她仅仅是 JavaScript”. 代码一一对应地编译到 JS, 不会在编译过程中进行解释. 已有的 JavaScript 类库可以无缝地和 CoffeeScript 搭配使用, 反之亦然. 编译后的代码是可读的, 且经过美化, 能在所有 JavaScript 环境中运行, 并且应该和对应手写的 JavaScript 一样快或者更快.

换而言之,CoffeeScript只是将JS给简化,使得JS的编码更加人性化。虽然CoffeeScript编译出来的JS看起来并不简单,但CoffeeScript编写的时候确实会减少不少代码量。具体可参照后文的例子。

CoffeeScript安装

CoffeeScript可以通过npm安装和管理。首先你要安装nodeJS,然后通过以下命令安装:

npm install -g coffee-script

coffee命令使用

安装之后, 你应该可以运行 coffee 命令以执行脚本, 编译 .coffee 文件到 .js 文件, 和提供一个交互式的 REPL. coffee命令有下列参数:

参数 作用
-c, –compile 编译一个 .coffee 脚本到一个同名的 .js 文件
-m, –map 随 JavaScript 文件一起生成 source maps. 并且在 JavaScript 里加上 sourceMappingURL 指令
-i, –interactive 启动一个交互式的 CoffeeScript 会话用来尝试一些代码片段. 等同于执行 coffee 而不加参数.
-o, –output [DIR] 将所有编译后的 JavaScript 文件写到指定文件夹. 与 –compile 或 –watch 搭配使用.
-j, –join [FILE] 编译之前, 按参数传入顺序连接所有脚本到一起, 编译后写到指定的文件. 对于编译大型项目有用.
-w, –watch 监视文件改变, 任何文件更新时重新执行命令.
-p, –print JavaScript 直接打印到 stdout 而不是写到一个文件.
-s, –stdio 将 CoffeeScript 传递到 STDIN 后从 STDOUT 获取 JavaScript. 对其他语言写的进程有好处. 比如:cat src/cake.coffee coffee -sc
-l, –literate 将代码作为 Literate CoffeeScript 解析. 只会在从 stdio 直接传入代码或者处理某些没有后缀的文件名需要写明这点.
-e, –eval 直接从命令行编译和打印一小段 CoffeeScript. 比如:coffee -e "console.log num for num in [10..1]"
-b, –bare 编译到 JavaScript 时去掉顶层函数的包裹.
-t, –tokens 不对 CoffeeScript 进行解析, 仅仅进行 lex, 打印出 token stream: [IDENTIFIER square] [ASSIGN =] [PARAM_START (]
–nodejs node 命令有一些实用的参数, 比如--debug, --debug-brk, --max-stack-size, 和 --expose-gc. 用这个参数直接把参数转发到 Node.js. 重复使用 --nodejs 来传递多个参数.
-n, –nodes 不对 CoffeeScript 进行编译, 仅仅 lex 和解析, 打印 parse tree:
ExpressionsAssignValue "square"Code "x"Op *Value "x"Value "x"

函数

函数体组成

coffee的函数通过一组可选的圆括号包裹的参数, 一个箭头, 一个函数体来定义

square = (x) -> x * x

等同于

square = function(x) {return x * x;
};

返回值

函数体的最后一条表达式会作为返回值返回。

无参数情况

如果不需要参数,则可以省略参数列表,表示为

square = -> 'empty'

相当于

square = function() {return 'empty';
};

多参数和默认值

如果需要多个参数,只需要按(参数1,参数2…)的形式写就好。
参数可以设置默认值,例如

add = (x = 4, y = 3) -> x + y
add 5

等同于

add = function(x, y) {if (x == null) {x = 4;}if (y == null) {y = 3;}return x + y;
};add(5);

闭包表示

函数还有另一种表示方法,就是用=>代替->,两者的区别在于前者会解释成闭包函数

square = (x) => x * x

解释为:

square = (function(_this) {return function(x) {return x * x;};
})(this);

变参

使用 JavaScript 的 arguments 对象是一种处理接收不定数量个参数的函数常用办法. CoffeeScript 在函数定义和调用里提供了变参(splats) ...的语法, 让不定个数的参数使用起来更愉悦一些.

gold = silver = rest = "unknown"awardMedals = (first, second, others...) ->gold   = firstsilver = secondrest   = otherscontenders = ["Michael Phelps""Liu Xiang""Yao Ming""Usain Bolt"
]awardMedals contenders... alert "The Field: " + rest

解释为:

var awardMedals, contenders, gold, rest, silver,__slice = [].slice;gold = silver = rest = "unknown";awardMedals = function() {var first, others, second;first = arguments[0], second = arguments[1], others = 3 <= arguments.length ? __slice.call(arguments, 2) : [];gold = first;silver = second;
  return rest = others;
};contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Usain Bolt"];awardMedals.apply(null, contenders);alert("The Field: " + rest);

输出为:[“Yao Ming”, “Usain Bolt”]

注意:参数调用的时候需要在后面加上...,否则会只传入一个参数

词法作用域和变量安全

CoffeeScript 编译器会考虑所有变量, 保证每个变量都在词法域里适当地被定义 — 你永远不需要自己去写 var.

outer = 1
changeNumbers = ->inner = -1outer = 10
inner = changeNumbers()

解释为:

var changeNumbers, inner, outer;outer = 1;changeNumbers = function() {var inner;inner = -1;return outer = 10;
};inner = changeNumbers();

所有变量的定义都被推到相关的顶层作用域, 也就是第一次出现的位置. outer 在内层的函数里没有被重新定义, 因为它已经存在于作用域当中了. 同时, 内层函数里的 inner 不应该改变外部的同名的变量, 所以在这里有自己的声明.

条件语句

这部分比较好理解,直接看代码就好。不同的一点是coffee中有了unless关键字,等同于 if not

mood = greatlyImproved if singingunless happy and knowsItclapsHands()chaChaCha()
elseshowIt()date = if friday then sue else jill

相当于

var date, mood;if (singing) {mood = greatlyImproved;
}if (!(happy && knowsIt)) {clapsHands();chaChaCha();
} else {showIt();
}date = friday ? sue : jill;

在 while, if/else, switch/when 的语句当中, then 可以被用来分隔判断条件跟表达式, 这样就不用强制写换行或者分号了.

循环

数组和对象

在很多时候,换行可以代替,

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

range

range生成数组

数组也可以通过范围来表示,例如

a = [1..10]
a = [1...10]

分别编译为

a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
a = [1, 2, 3, 4, 5, 6, 7, 8, 9];

也就是,..是包括最后一个数据,而...不包括最后一个数据。

数组切片

作用类似slice,和range生成数组有点相似,但作为下标范围时两端都有缺省值,左边缺省时默认为0,右边缺省时默认为最大下标。返回值是切割后的数组。

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]start   = numbers[0..2]
middle  = numbers[3...-2]
end     = numbers[-2..]
copy    = numbers[..]
numbers[2..5] = [-3, -4, -5, -6]

编译为:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];start = numbers.slice(0, 3); // [1,2,3]
middle = numbers.slice(3, -2); // [4,5,6,7]
end = numbers.slice(-2); // [8,9]
copy = numbers.slice(0); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
[].splice.apply(numbers, [2, 4].concat(_ref = [-3, -4, -5, -6])), _ref; // [1, 2, -3, -4, -5, -6, 7, 8, 9]

循环

eat food for food in ['toast', 'cheese', 'wine']courses = ['greens', 'caviar', 'truffles', 'roast', 'cake']
menu i + 1, dish for dish, i in coursesfoods = ['broccoli', 'spinach', 'chocolate']
eat food for food in foods when food isnt 'chocolate'

编译为:

var courses, dish, food, foods, i, _i, _j, _k, _len, _len1, _len2, _ref;_ref = ['toast', 'cheese', 'wine'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {food = _ref[_i];eat(food);
}courses = ['greens', 'caviar', 'truffles', 'roast', 'cake'];for (i = _j = 0, _len1 = courses.length; _j < _len1; i = ++_j) {dish = courses[i];menu(i + 1, dish);
}foods = ['broccoli', 'spinach', 'chocolate'];for (_k = 0, _len2 = foods.length; _k < _len2; _k++) {food = foods[_k];if (food !== 'chocolate') {eat(food);}
}

遍历

数组遍历

nums = []
for num, i in nums

其中,i是下标。效果等同于:

nums = []
for (i = _i = 0, _len = nums.length; _i < _len; i = ++_i) {num = nums[i];
}

数组遍历还可以加by,达到固定跨度迭代的目的。例如:

nums = []
for num, i in nums by 2

对象属性遍历

yearsOld = max: 10, ida: 9, tim: 11ages = for child, age of yearsOld"#{child} is #{age}"

编译为:

var age, ages, child, yearsOld;yearsOld = {max: 10,ida: 9,tim: 11
};ages = (function() {var _results;_results = [];for (child in yearsOld) {age = yearsOld[child];_results.push("" + child + " is " + age);}return _results;
})();

如果你希望仅迭代在当前对象中定义的属性,通过hasOwnProperty检查并 避免属性是继承来的,可以这样来写:

for own key, value of object

例如,上例可以改为

ages = for child, age of yearsOld

编译出来的结果就变成:

var age, ages, child, yearsOld,__hasProp = {}.hasOwnProperty;yearsOld = {max: 10,ida: 9,tim: 11
};ages = (function() {var _results;_results = [];for (child in yearsOld) {if (!__hasProp.call(yearsOld, child)) continue;age = yearsOld[child];_results.push("" + child + " is " + age);}return _results;
})();

底层循环

CoffeeScript仅提供了一种底层循环,即while循环。与JavaScript中的while 循环的主要区别是,在CoffeeScript中while可以作为表达式来使用, 而且可以返回一个数组,该数组包含每个迭代项的迭代结果。
CoffeeScript还提供了另一个关键字,until,效果等同于while not

buy()  while supply > demand
sell() until supply > demand

等同于

while (supply > demand) {buy();
}
while (!(supply > demand)) {sell();
}

遍历闭包

使用 JavaScript 循环生成函数的时候, 经常会添加一个闭包来包裹代码, 这样做目的是为了循环的变量被保存起来, 而不是所有生成的函数搜去访问最后一个循环的变量. CoffeeScript 提供了一个 do 关键字, 用来直接调用跟在后边的函数, 并且传递需要的参数.

for filename in listdo (filename) ->fs.readFile filename, (err, contents) ->compile filename, contents.toString()

编译为:

var filename, _fn, _i, _len;_fn = function(filename) {return fs.readFile(filename, function(err, contents) {return compile(filename, contents.toString());});
};
for (_i = 0, _len = list.length; _i < _len; _i++) {filename = list[_i];_fn(filename);
}

有些代码在 JavaScript 当中要写不少的语句, 而在 CoffeeScript 中只是表达式的一部分, 这些代码的编译结果会自动生成一个闭包. 这个写法很有用, 比如把列表解析的结果赋值给变量:

globals = (name for name of window)[0...10]

编译为:

var globals, name;globals = ((function() {var _results;_results = [];for (name in window) {_results.push(name);}return _results;
})()).slice(0, 10);

操作符

算术操作符/逻辑操作符

由于操作符 == 常常带来不准确的约束, 不容易达到效果, 而且跟其他语言当中意思不一致, CoffeeScript 会把 == 编译为 ===, 把 != 变异为 !==. 此外, is 编译我 ===, 而 isnt 编译为!==.

not可以作为 !的 alias 使用.

逻辑操作方面, and编译为&&, 而 or编译为||.

onyestrue 是一样的, 而 offno 是布尔值 false.

unless 可以认为是 if 相反的版本.

this.property 简短的写法可以用 @property.

可以用 in 判断数据在数组中是否出现, 而 of 可以探测 JavaScript 对象的属性是否存在.

为了简化数学表达式, ** 可以用来表示乘方, // 表示整除, %% 提供数学的模运算(译注: true mathematical modulo?).

完整的列表:

存在性操作符

在 JavaScript 里检测一个变量的存在性有点麻烦. if (variable) ... 比较接近答案, 但是对 0 不成立. CoffeeScript 的存在性操作符?除非是null或者 undefined, 否则都返回 true

例如:

solipsism = true if mind? and not world?speed = 0
speed ?= 15footprints = yeti ? "bear"

编译为:

var footprints, solipsism, speed;if ((typeof mind !== "undefined" && mind !== null) && (typeof world === "undefined" || world === null)) {solipsism = true;
}speed = 0;if (speed == null) {speed = 15;
}footprints = typeof yeti !== "undefined" && yeti !== null ? yeti : "bear";

存在性操作符 ?. 的访问器的变体可以用来吸收链式属性调用中的 null. 数据可能是 null 或者 undefined 的情况下可以用这种写法替代访问器.。 如果所有属性都存在, 那么你会得到想要的结果, 如果链式调用有问题, 会返回 undefined 而不是抛出 TypeError.

zip = lottery.drawWinner?().address?.zipcode

编译为:

var zip, _ref;zip = typeof lottery.drawWinner === "function" ? (_ref = lottery.drawWinner().address) != null ? _ref.zipcode : void 0 : void 0;

继承

CoffeeScript 提供了一个基础的class结构, 你可以在一个定义的表达式里完成命名 class, 定义父类, 赋值原型上的属性, 定义构造器。同时用@attr表示this.attr

class Animalconstructor: (@name) ->move: (meters) ->alert @name + " moved #{meters}m."class Snake extends Animalmove: ->alert "Slithering..."super 5class Horse extends Animalmove: ->alert "Galloping..."super 45sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"sam.move()
tom.move()

相当于:

var Animal, Horse, Snake, sam, tom,__hasProp = {}.hasOwnProperty,__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };Animal = (function() {function Animal(name) {this.name = name;}Animal.prototype.move = function(meters) {return alert(this.name + (" moved " + meters + "m."));};return Animal;})();Snake = (function(_super) {__extends(Snake, _super);function Snake() {return Snake.__super__.constructor.apply(this, arguments);}Snake.prototype.move = function() {alert("Slithering...");return Snake.__super__.move.call(this, 5);};return Snake;})(Animal);Horse = (function(_super) {__extends(Horse, _super);function Horse() {return Horse.__super__.constructor.apply(this, arguments);}Horse.prototype.move = function() {alert("Galloping...");return Horse.__super__.move.call(this, 45);};return Horse;})(Animal);sam = new Snake("Sammy the Python");tom = new Horse("Tommy the Palomino");sam.move();tom.move();

如果你不喜欢用 class 的裁判法定义原型, CoffeeScript 提供了一些低级的方便写法. extends 操作符可以用来恰当地定义任何一对构造函数的原型链; 用 :: 可以快速访问对象的原型; super() 可以编译为一个父类上同名方法的调用.

String::dasherize = ->this.replace /_/g, "-"

等价于:

 String.prototype.dasherize = function() {return this.replace(/_/g, "-");};

其他

还有一些零碎的特性,例如字符串替换、块级正则表达式、块级字符串等特性,在这里不特殊说明了,建议查看官方文档

CoffeeScript入门相关推荐

  1. CoffeeScript入门实践

    原文来自 http://www.w3ctech.com/p/1065 在这一期的节目中,我们将介绍下CoffeeScript, 那么什么是CoffeeScript呢?CoffeeScript是一门小语 ...

  2. CoffeeScript 简介与安装步骤概览

    CoffeeScript 简介与安装步骤概览 一.关于CoffeeScript CoffeeScript是一门新兴的构建在JavaScript之上的预处理器语言,经过编译可以生成高效的JavaScri ...

  3. CoffeeScript快速入门

    CoffeeScript快速入门 CoffeeScript是最近比较流行的一个小的编程语言,它有自己的语法(受Python和Ruby影响比较多,个人觉得更象Ruby),其编译器将其编译输出javasc ...

  4. CoffeeScript - CoffeeScript安装使用入门

    CoffeeScript英文官网: http://coffeescript.org/ CoffeeScript中文官网: http://coffee-script.org/ CoffeeScript简 ...

  5. coffeescript语法入门

    ref:http://www.dotblogs.com.tw/alonstar/archive/2011/08/31/coffeescript_syntax.aspx 昨天花了點時間把官網的都玩過之後 ...

  6. [CoffeeScript]咖啡,入门

     简介: 目前,大家对 CoffeeScript 狂热有加.CoffeeScript 是一种新的编程语言,构建于 JavaScript 之上.CoffeeScript 提供了一种简洁的语法,对 P ...

  7. windows下nodejs express安装及入门网站,视频资料,开源项目介绍

    windows下nodejs express安装及入门网站,视频资料,开源项目介绍,pm2,supervisor,npm,Pomelo,Grunt安装使用注意事项等总结 第一步:下载安装文件 下载地址 ...

  8. Node.js入门(含NVM、NPM、NVM的安装)

    本文最初发表于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文. Node.js的介绍 引擎 引擎的特性: JS的内核即引擎.因为引擎有以 ...

  9. webpack简易入门

    webpack简易学习教程 1.为什么要用webpack? 模块化开发 代码压缩,合并,提取公共代码, 编译ES6,CoffeeScript: Scss,Less等css预处理器 2.webpack简 ...

最新文章

  1. CSS_03_04_CSS伪元素选择器
  2. Three.js学习笔记
  3. Rows Over Window与Range Over Window的区别
  4. 基因行业容器存储解决方案
  5. backupexec mysql_MySQL备份可能遇到的坑
  6. 通过NRM随意切换Taobao镜像和NPM官方镜像
  7. Linux/Centos源码安装python3任意版本
  8. ArrayList在foreach正常迭代删除不报错的原因
  9. jsonp实现跨域问题
  10. 知识图谱嵌入的应用场景
  11. torch.eq(input, other, out=None)
  12. WINDOWS下git的安装与使用
  13. 【qcom msm8953 android712】rtc 调试分析续
  14. 数据库中违反外键约束的情况
  15. springboot如何自定义starter
  16. python基础什么是函数的定义及用法
  17. 小项目之个人银行账户管理程序(1)
  18. 三子棋(井字棋) 保姆级详解
  19. HEVC代码学习——帧间预测:预测MV获取(xEstimateMvPredAMVP、fillMVPCand)
  20. 百度地图、高德地图、腾讯地图餐饮POI内容结构及获取接口

热门文章

  1. 用java做一个简单的打字游戏
  2. C语言中的 |= 意思
  3. ubuntu16.04、2080Ti、Caffe从0开始安装
  4. 前端动态生成横向树形表格
  5. Visual Studio 2005 SP1集成(整理前辈版本)
  6. 车辆运动控制(4)考虑道路曲率和轮胎滑移
  7. c语言 跨子程序跳转,PLC的条件跳转,子程序调用等指令使用方法
  8. uni app 安装 vconsole
  9. 个人备案的网站能放企业服务器吗,个人及企业域名备案对网站有什么影响
  10. 慕课网跟练系列——侧边栏导航跟随案例