CoffeeScript入门
官网文档传送门
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
编译为||
.
on
和 yes
跟 true
是一样的, 而 off
和 no
是布尔值 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入门相关推荐
- CoffeeScript入门实践
原文来自 http://www.w3ctech.com/p/1065 在这一期的节目中,我们将介绍下CoffeeScript, 那么什么是CoffeeScript呢?CoffeeScript是一门小语 ...
- CoffeeScript 简介与安装步骤概览
CoffeeScript 简介与安装步骤概览 一.关于CoffeeScript CoffeeScript是一门新兴的构建在JavaScript之上的预处理器语言,经过编译可以生成高效的JavaScri ...
- CoffeeScript快速入门
CoffeeScript快速入门 CoffeeScript是最近比较流行的一个小的编程语言,它有自己的语法(受Python和Ruby影响比较多,个人觉得更象Ruby),其编译器将其编译输出javasc ...
- CoffeeScript - CoffeeScript安装使用入门
CoffeeScript英文官网: http://coffeescript.org/ CoffeeScript中文官网: http://coffee-script.org/ CoffeeScript简 ...
- coffeescript语法入门
ref:http://www.dotblogs.com.tw/alonstar/archive/2011/08/31/coffeescript_syntax.aspx 昨天花了點時間把官網的都玩過之後 ...
- [CoffeeScript]咖啡,入门
简介: 目前,大家对 CoffeeScript 狂热有加.CoffeeScript 是一种新的编程语言,构建于 JavaScript 之上.CoffeeScript 提供了一种简洁的语法,对 P ...
- windows下nodejs express安装及入门网站,视频资料,开源项目介绍
windows下nodejs express安装及入门网站,视频资料,开源项目介绍,pm2,supervisor,npm,Pomelo,Grunt安装使用注意事项等总结 第一步:下载安装文件 下载地址 ...
- Node.js入门(含NVM、NPM、NVM的安装)
本文最初发表于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文. Node.js的介绍 引擎 引擎的特性: JS的内核即引擎.因为引擎有以 ...
- webpack简易入门
webpack简易学习教程 1.为什么要用webpack? 模块化开发 代码压缩,合并,提取公共代码, 编译ES6,CoffeeScript: Scss,Less等css预处理器 2.webpack简 ...
最新文章
- CSS_03_04_CSS伪元素选择器
- Three.js学习笔记
- Rows Over Window与Range Over Window的区别
- 基因行业容器存储解决方案
- backupexec mysql_MySQL备份可能遇到的坑
- 通过NRM随意切换Taobao镜像和NPM官方镜像
- Linux/Centos源码安装python3任意版本
- ArrayList在foreach正常迭代删除不报错的原因
- jsonp实现跨域问题
- 知识图谱嵌入的应用场景
- torch.eq(input, other, out=None)
- WINDOWS下git的安装与使用
- 【qcom msm8953 android712】rtc 调试分析续
- 数据库中违反外键约束的情况
- springboot如何自定义starter
- python基础什么是函数的定义及用法
- 小项目之个人银行账户管理程序(1)
- 三子棋(井字棋) 保姆级详解
- HEVC代码学习——帧间预测:预测MV获取(xEstimateMvPredAMVP、fillMVPCand)
- 百度地图、高德地图、腾讯地图餐饮POI内容结构及获取接口