CoffeeScript 是一种编译成 JavaScript 的小型语言。 在那笨拙的 Java 风格的外表下,JavaScript 一直拥有一颗美丽的心。CoffeeScript 试图以一种简单的方式暴露 JavaScript 的优点。

CoffeeScript 的黄金法则:“它只是 JavaScript”。代码一一编译成等效的 JS,并且在运行时没有解释。您可以从 CoffeeScript(反之亦然)无缝地使用任何现有的 JavaScript 库。编译后的输出是可读的、格式良好的,并且往往比等效的手写 JavaScript 运行得更快或一样快。

CoffeeScript 编译器尽力生成在所有 JavaScript 运行时中运行的输出 JavaScript,但存在例外。仅当您知道您的 目标运行时可以支持它们 时,才使用 生成器函数for…from带标签的模板字面量。如果您使用 模块,您将需要 使用其他工具来解析它们

最新 1.x 版本: 1.12.7

npm install -g coffeescript

升级到 CoffeeScript 2! 它添加了对 ES2015 类async/awaitJSX对象剩余/展开语法使用现代语法生成的 JavaScript 的支持。 了解更多

概述

左侧是 CoffeeScript,右侧是编译后的 JavaScript 输出。

# Assignment:
number   = 42
opposite = true

# Conditions:
number = -42 if opposite

# Functions:
square = (x) -> x * x

# Arrays:
list = [1, 2, 3, 4, 5]

# Objects:
math =
  root:   Math.sqrt
  square: square
  cube:   (x) -> x * square x

# Splats:
race = (winner, runners...) ->
  print winner, runners

# Existence:
alert "I knew it!" if elvis?

# Array comprehensions:
cubes = (math.cube num for num in list)
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;
})();
运行:cubes

安装

coffee 的命令行版本可作为 Node.js 实用程序使用。但是,核心编译器 不依赖于 Node,可以在任何 JavaScript 环境或浏览器中运行(参见 尝试 CoffeeScript)。

要安装,首先确保您拥有 Node.js 的最新稳定版本的有效副本。然后,您可以使用 npm 全局安装 CoffeeScript

npm install --global coffeescript

这将使 coffeecake 命令在全局范围内可用。

当您需要 CoffeeScript 作为项目的依赖项时,您可以在该项目的文件夹中本地安装它

npm install --save coffeescript

coffeecake 命令将首先查看当前文件夹以查看是否本地安装了 CoffeeScript,如果安装了,则使用该版本。这允许在全局和本地安装不同版本的 CoffeeScript。

使用

安装后,您应该可以访问 coffee 命令,该命令可以执行脚本、将 .coffee 文件编译成 .js,并提供交互式 REPL。coffee 命令接受以下选项

选项 描述
-c, --compile .coffee 脚本编译成同名 .js JavaScript 文件。
-m, --map 在编译后的 JavaScript 文件旁边生成源映射。还会向 JavaScript 添加 sourceMappingURL 指令。
-M, --inline-map --map 相同,但将源映射直接包含在编译后的 JavaScript 文件中,而不是在单独的文件中。
-i, --interactive 启动交互式 CoffeeScript 会话以尝试简短的代码片段。与不带参数调用 coffee 相同。
-o, --output [DIR] 将所有编译后的 JavaScript 文件写入指定的目录。与 --compile--watch 结合使用。
-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]"
-r, --require [MODULE] 在启动 REPL 或使用 --eval 标志评估给定的代码之前,require() 给定的模块。
-b, --bare 编译 JavaScript,不使用 顶层函数安全包装器
-t, --tokens 不解析 CoffeeScript,而是对其进行词法分析,并打印出标记流。用于调试编译器。
-n, --nodes 不编译 CoffeeScript,而是对其进行词法分析和解析,并打印出解析树。用于调试编译器。
--nodejs node 可执行文件有一些有用的选项,您可以设置这些选项,例如 --debug--debug-brk--max-stack-size--expose-gc。使用此标志将选项直接转发到 Node.js。要传递多个标志,请多次使用 --nodejs
--no-header 抑制“由 CoffeeScript 生成”标题。

示例

Literate CoffeeScript

除了用作普通编程语言外,CoffeeScript 还可以以“literate”模式编写。如果您将文件命名为 .litcoffee 扩展名,您可以将其编写为 Markdown 文档——一个也恰好是可执行 CoffeeScript 代码的文档。编译器将把任何缩进的块(Markdown 表示源代码的方式)视为代码,并将其余部分视为注释。

仅仅为了好玩,编译器的一小部分目前以这种方式实现:请参见 作为文档原始在文本编辑器中正确突出显示

语言参考

本参考的结构使您可以从上到下阅读,如果您愿意。后面的部分使用之前介绍的概念和语法。假设您熟悉 JavaScript。在以下所有示例中,左侧提供源 CoffeeScript,右侧提供直接编译成 JavaScript 的内容。

许多示例可以通过按下右侧的 运行 按钮来运行(在有意义的情况下),并且可以通过按下左侧的 加载 按钮加载到“尝试 CoffeeScript”控制台中。

首先,基础知识:CoffeeScript 使用显着空格来分隔代码块。您不需要使用分号 ; 来终止表达式,结束行就可以了(尽管仍然可以使用分号将多个表达式放在一行上)。不使用花括号 { } 来包围 函数if 语句switchtry/catch 中的代码块,而是使用缩进。

如果您传递参数,则不需要使用括号来调用函数。隐式调用会向前包装到行或块表达式的末尾。
console.log sys.inspect objectconsole.log(sys.inspect(object));

函数

函数由括号中的可选参数列表、箭头和函数体定义。空函数如下所示:->

square = (x) -> x * x
cube   = (x) -> square(x) * x
var cube, square;

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

cube = function(x) {
  return square(x) * x;
};
加载
运行:cube(5)

函数也可以为参数设置默认值,如果传入的参数缺失(nullundefined),则将使用这些默认值。

fill = (container, liquid = "coffee") ->
  "Filling the #{container} with #{liquid}..."
var fill;

fill = function(container, liquid) {
  if (liquid == null) {
    liquid = "coffee";
  }
  return "Filling the " + container + " with " + liquid + "...";
};
加载
运行:fill("cup")

对象和数组

CoffeeScript 中的对象和数组字面量看起来与它们的 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
  }
};
加载
运行:song.join(" … ")

在 JavaScript 中,您不能使用保留字(如 class)作为对象的属性,除非将它们作为字符串引用。CoffeeScript 会注意到对象中用作键的保留字并为您引用它们,因此您不必担心(例如,在使用 jQuery 时)。

$('.account').attr class: 'active'

log object.class
$('.account').attr({
  "class": 'active'
});

log(object["class"]);
加载

当您希望使用相同名称的变量设置键时,CoffeeScript 有一种创建对象的快捷方式。

name = "Michelangelo"
mask = "orange"
weapon = "nunchuks"
turtle = {name, mask, weapon}
output = "#{turtle.name} wears an #{turtle.mask} mask. Watch out for his #{turtle.weapon}!"
var mask, name, output, turtle, weapon;

name = "Michelangelo";

mask = "orange";

weapon = "nunchuks";

turtle = {
  name: name,
  mask: mask,
  weapon: weapon
};

output = turtle.name + " wears an " + turtle.mask + " mask. Watch out for his " + turtle.weapon + "!";
加载

词法作用域和变量安全

CoffeeScript 编译器会确保所有变量都在词法作用域内正确声明——您永远不需要自己编写 var

outer = 1
changeNumbers = ->
  inner = -1
  outer = 10
inner = changeNumbers()
var changeNumbers, inner, outer;

outer = 1;

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

inner = changeNumbers();
加载
运行:inner

注意所有变量声明都被推到最近作用域的顶部,它们第一次出现的地方。outer 在内部函数中没有重新声明,因为它已经在作用域内;另一方面,函数内的 inner 不应该能够改变同名外部变量的值,因此它有自己的声明。

这种行为实际上与 Ruby 的局部变量作用域相同。因为你无法直接访问 var 关键字,所以不可能故意遮蔽外部变量,你只能引用它。因此,如果你正在编写一个深度嵌套的函数,请小心不要意外地重复使用外部变量的名称。

虽然为了清晰起见,在本文档中被抑制,但所有 CoffeeScript 输出都包装在一个匿名函数中:(function(){ … })(); 这种安全包装,结合自动生成的 var 关键字,使得意外污染全局命名空间变得极其困难。

如果你想创建供其他脚本使用的顶级变量,将它们作为属性附加到 window 上;将它们作为属性附加到 CommonJS 中的 exports 对象上;或者使用 export 语句。如果你同时针对 CommonJS 和浏览器,存在运算符(下面会介绍)为你提供了一种可靠的方法来确定在哪里添加它们:exports ? this

If、Else、Unless 和条件赋值

If/else 语句可以在不使用括号和花括号的情况下编写。与函数和其他块表达式一样,多行条件由缩进分隔。还有一种方便的后缀形式,在末尾使用 ifunless

CoffeeScript 可以使用三元运算符(如果可能)和闭包包装(否则)将 if 语句编译成 JavaScript 表达式。CoffeeScript 中没有显式的三元语句——你只需在单行上使用常规的 if 语句。

mood = greatlyImproved if singing

if happy and knowsIt
  clapsHands()
  chaChaCha()
else
  showIt()

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;
加载

散点图…

JavaScript arguments 对象是处理接受可变数量参数的函数的一种有用方法。CoffeeScript 提供了散点图 ...,用于函数定义和调用,使可变数量的参数更易于接受。

gold = silver = rest = "unknown"

awardMedals = (first, second, others...) ->
  gold   = first
  silver = second
  rest   = others

contenders = [
  "Michael Phelps"
  "Liu Xiang"
  "Yao Ming"
  "Allyson Felix"
  "Shawn Johnson"
  "Roman Sebrle"
  "Guo Jingjing"
  "Tyson Gay"
  "Asafa Powell"
  "Usain Bolt"
]

awardMedals contenders...

alert "Gold: " + gold
alert "Silver: " + silver
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", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"];

awardMedals.apply(null, contenders);

alert("Gold: " + gold);

alert("Silver: " + silver);

alert("The Field: " + rest);
加载
运行

循环和推导

你在 CoffeeScript 中编写的多数循环将是针对数组、对象和范围的 推导。推导用(并编译成)for 循环替换,并带有可选的保护子句和当前数组索引的值。与 for 循环不同,数组推导是表达式,可以返回和赋值。

# Eat lunch.
eat food for food in ['toast', 'cheese', 'wine']

# Fine five course dining.
courses = ['greens', 'caviar', 'truffles', 'roast', 'cake']
menu i + 1, dish for dish, i in courses

# Health conscious meal.
foods = ['broccoli', 'spinach', 'chocolate']
eat food for food in foods when food isnt 'chocolate'
var courses, dish, food, foods, i, j, k, l, len, len1, len2, ref;

ref = ['toast', 'cheese', 'wine'];
for (j = 0, len = ref.length; j < len; j++) {
  food = ref[j];
  eat(food);
}

courses = ['greens', 'caviar', 'truffles', 'roast', 'cake'];

for (i = k = 0, len1 = courses.length; k < len1; i = ++k) {
  dish = courses[i];
  menu(i + 1, dish);
}

foods = ['broccoli', 'spinach', 'chocolate'];

for (l = 0, len2 = foods.length; l < len2; l++) {
  food = foods[l];
  if (food !== 'chocolate') {
    eat(food);
  }
}
加载

例如,推导应该能够处理大多数你原本会使用循环、each/forEachmapselect/filter 的地方
shortNames = (name for name in list when name.length < 5)
如果你知道循环的开始和结束,或者想要以固定大小的增量逐步执行,可以使用范围来指定推导的开始和结束。

countdown = (num for num in [10..1])
var countdown, num;

countdown = (function() {
  var i, results;
  results = [];
  for (num = i = 10; i >= 1; num = --i) {
    results.push(num);
  }
  return results;
})();
加载
运行:countdown

请注意,由于我们在上面的示例中将推导的值赋值给一个变量,因此 CoffeeScript 将每次迭代的结果收集到一个数组中。有时函数以仅为其副作用而运行的循环结束。请小心,不要在这些情况下意外地返回推导的结果,方法是在函数底部添加一个有意义的返回值——比如 true——或 null

要以固定大小的块逐步执行范围推导,请使用 by,例如:evens = (x for x in [0..10] by 2)

如果你不需要当前迭代值,可以省略它:browser.closeCurrentTab() for [0...count]

推导也可以用于遍历对象中的键和值。使用 of 来表示对对象的属性而不是数组中的值的推导。

yearsOld = max: 10, ida: 9, tim: 11

ages = 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;
})();
加载
运行:ages.join(", ")

如果你想只遍历对象本身定义的键,可以通过添加 hasOwnProperty 检查来避免可能从原型继承的属性,使用 for own key, value of object

要迭代生成器函数,请使用 from。请参阅 生成器函数

CoffeeScript 提供的唯一低级循环是 while 循环。与 JavaScript 的主要区别在于 while 循环可以用作表达式,返回一个包含循环中每次迭代结果的数组。

# Econ 101
if this.studyingEconomics
  buy()  while supply > demand
  sell() until supply > demand

# Nursery Rhyme
num = 6
lyrics = while num -= 1
  "#{num} little monkeys, jumping on the bed.
    One fell out and bumped his head."
var lyrics, num;

if (this.studyingEconomics) {
  while (supply > demand) {
    buy();
  }
  while (!(supply > demand)) {
    sell();
  }
}

num = 6;

lyrics = (function() {
  var results;
  results = [];
  while (num -= 1) {
    results.push(num + " little monkeys, jumping on the bed. One fell out and bumped his head.");
  }
  return results;
})();
加载
运行:lyrics.join("\n")

为了可读性,until 关键字等效于 while notloop 关键字等效于 while true

当使用 JavaScript 循环生成函数时,通常会插入一个闭包包装器,以确保闭包循环变量,并且所有生成的函数不会只共享最终值。CoffeeScript 提供了 do 关键字,它会立即调用传递的函数,并转发任何参数。

for filename in list
  do (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);
}
加载

使用范围进行数组切片和拼接

范围也可以用于提取数组的切片。使用两个点 (3..6),范围是包含的 (3, 4, 5, 6);使用三个点 (3...6),范围不包含末尾 (3, 4, 5)。切片索引具有有用的默认值。省略的第一个索引默认为零,省略的第二个索引默认为数组的大小。

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

start   = numbers[0..2]

middle  = numbers[3...-2]

end     = numbers[-2..]

copy    = numbers[..]
var copy, end, middle, numbers, start;

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

start = numbers.slice(0, 3);

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

end = numbers.slice(-2);

copy = numbers.slice(0);
加载
运行:middle

相同的语法可以与赋值一起使用,用新值替换数组的一部分,对其进行拼接。

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

numbers[3..6] = [-3, -4, -5, -6]
var numbers, ref;

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

[].splice.apply(numbers, [3, 4].concat(ref = [-3, -4, -5, -6])), ref;
加载
运行:numbers

请注意,JavaScript 字符串是不可变的,不能拼接。

一切都是表达式(至少,尽可能地)

你可能已经注意到,即使我们没有在 CoffeeScript 函数中添加 return 语句,它们仍然会返回其最终值。CoffeeScript 编译器试图确保语言中的所有语句都可以用作表达式。观察下面的函数中 return 如何被推送到每个可能的执行分支中。

grade = (student) ->
  if student.excellentWork
    "A+"
  else if student.okayStuff
    if student.triedHard then "B" else "B-"
  else
    "C"

eldest = if 24 > 21 then "Liz" else "Ike"
var eldest, grade;

grade = function(student) {
  if (student.excellentWork) {
    return "A+";
  } else if (student.okayStuff) {
    if (student.triedHard) {
      return "B";
    } else {
      return "B-";
    }
  } else {
    return "C";
  }
};

eldest = 24 > 21 ? "Liz" : "Ike";
加载
运行:eldest

即使函数总是会返回其最终值,但当你确定已经完成时,在函数体中显式地写出 return (return value) 既是可能的,也是鼓励的。

由于变量声明发生在作用域的顶部,因此即使是以前从未见过的变量,也可以在表达式中使用赋值

six = (one = 1) + (two = 2) + (three = 3)
var one, six, three, two;

six = (one = 1) + (two = 2) + (three = 3);
加载
运行:six

在 CoffeeScript 中,原本是 JavaScript 中的语句,当用作表达式的一部分时,会通过将它们包装在闭包中来转换为表达式。这使你可以做一些有用的事情,比如将推导的结果赋值给一个变量

# The first ten global properties.

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);
加载
运行:globals

以及一些愚蠢的事情,比如将 try/catch 语句直接传递给函数调用

alert(
  try
    nonexistent / undefined
  catch error
    "And the error is ... #{error}"
)
var error;

alert((function() {
  try {
    return nonexistent / void 0;
  } catch (error1) {
    error = error1;
    return "And the error is ... " + error;
  }
})());
加载
运行

JavaScript 中有一些语句无法有意义地转换为表达式,即 breakcontinuereturn。如果你在代码块中使用它们,CoffeeScript 不会尝试执行转换。

运算符和别名

由于 == 运算符经常会导致不希望的强制转换,是不及物的,并且与其他语言中的含义不同,因此 CoffeeScript 将 == 编译成 ===,将 != 编译成 !==。此外,is 编译成 ===isnt 编译成 !==

你可以使用 not 作为 ! 的别名。

对于逻辑,and 编译成 &&or 编译成 ||

whileif/elseswitch/when 语句中,可以使用 then 来分隔条件和表达式,而不是换行符或分号。

YAML 中一样,onyes 与布尔值 true 相同,而 offno 与布尔值 false 相同。

unless 可以用作 if 的反义词。

作为 this.property 的快捷方式,你可以使用 @property

你可以使用 in 来测试数组是否存在,使用 of 来测试 JavaScript 对象键是否存在。

为了简化数学表达式,** 可以用于求幂,// 用于执行整数除法。% 的工作原理与 JavaScript 中相同,而 %% 提供 “被除数依赖的模运算”

-7 % 5 == -2 # The remainder of 7 / 5
-7 %% 5 == 3 # n %% 5 is always between 0 and 4

tabs.selectTabAtIndex((tabs.currentIndex - count) %% tabs.length)
var modulo = function(a, b) { return (+a % (b = +b) + b) % b; };

-7 % 5 === -2;

modulo(-7, 5) === 3;

tabs.selectTabAtIndex(modulo(tabs.currentIndex - count, tabs.length));
加载

现在一起

CoffeeScript JavaScript
is ===
isnt !==
not !
and &&
or ||
trueyeson true
falsenooff false
@this this
of in
in 没有 JS 等效项
a ** b Math.pow(a, b)
a // b Math.floor(a / b)
a %% b (a % b + b) % b
launch() if ignition is on

volume = 10 if band isnt SpinalTap

letTheWildRumpusBegin() unless answer is no

if car.speed < limit then accelerate()

winner = yes if pick in [47, 92, 13]

print inspect "My name is #{@name}"
var volume, winner;

if (ignition === true) {
  launch();
}

if (band !== SpinalTap) {
  volume = 10;
}

if (answer !== false) {
  letTheWildRumpusBegin();
}

if (car.speed < limit) {
  accelerate();
}

if (pick === 47 || pick === 92 || pick === 13) {
  winner = true;
}

print(inspect("My name is " + this.name));
加载

存在运算符

在 JavaScript 中检查变量是否存在有点困难。if (variable) … 接近,但对于零、空字符串和 false 失败。CoffeeScript 的存在运算符 ? 返回 true,除非变量为 nullundefined,这使得它类似于 Ruby 的 nil?

它还可以用于比 ||= 提供的更安全的条件赋值,适用于你可能正在处理数字或字符串的情况。

solipsism = true if mind? and not world?

speed = 0
speed ?= 15

footprints = 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";
加载
运行:footprints

存在运算符的访问器变体 ?. 可用于吸收属性链中的空引用。在基值可能为 nullundefined 的情况下,使用它代替点访问器 .。如果所有属性都存在,那么你将获得预期的结果,如果链断开,则返回 undefined 而不是原本会引发的 TypeError

zip = lottery.drawWinner?().address?.zipcode
var ref, zip;

zip = typeof lottery.drawWinner === "function" ? (ref = lottery.drawWinner().address) != null ? ref.zipcode : void 0 : void 0;
加载

吸收空值类似于 Ruby 的 andand gem,以及 Groovy 中的 安全导航运算符

类、继承和 Super

JavaScript 的原型继承一直以来都是一个令人费解的问题,它有一整套库,在 JavaScript 的原型之上提供了更简洁的经典继承语法:Base2Prototype.jsJS.Class 等。这些库提供了语法糖,但内置的继承如果忽略几个小的例外,将是完全可用的:调用 super(原型对象的当前函数实现)很麻烦,正确设置原型链也很麻烦。

CoffeeScript 提供了一个基本的 class 结构,它允许你命名你的类,设置超类,分配原型属性,并在单个可赋值表达式中定义构造函数,而不是重复地将函数附加到原型上。

构造函数是命名的,以便更好地支持有用的堆栈跟踪。在下面的第一个类中,this.constructor.name is "Animal"

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()
var Animal, Horse, Snake, sam, tom,
  extend = 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; },
  hasProp = {}.hasOwnProperty;

Animal = (function() {
  function Animal(name) {
    this.name = name;
  }

  Animal.prototype.move = function(meters) {
    return alert(this.name + (" moved " + meters + "m."));
  };

  return Animal;

})();

Snake = (function(superClass) {
  extend(Snake, superClass);

  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(superClass) {
  extend(Horse, superClass);

  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();
加载
运行

如果你不喜欢以经典方式构建你的原型,CoffeeScript 提供了一些更低级的便利。extends 运算符有助于正确设置原型,并且可以用于在任何一对构造函数之间创建继承链;:: 使你能够快速访问对象的原型;super() 被转换为对直接祖先的同名方法的调用。

String::dasherize = ->
  this.replace /_/g, "-"
String.prototype.dasherize = function() {
  return this.replace(/_/g, "-");
};
加载
运行:"one_two".dasherize()

最后,类定义是可执行代码块,这为有趣的元编程提供了可能性。因为在类定义的上下文中,this 是类对象本身(构造函数),你可以使用 @property: value 来分配静态属性,并调用父类中定义的函数:@attr 'title', type: 'text'

解构赋值

就像 JavaScript(从 ES2015 开始),CoffeeScript 也有解构赋值语法。当你将数组或对象字面量赋值给一个值时,CoffeeScript 会将两边拆开并相互匹配,将右边的值赋值给左边的变量。在最简单的情况下,它可以用于并行赋值

theBait   = 1000
theSwitch = 0

[theBait, theSwitch] = [theSwitch, theBait]
var ref, theBait, theSwitch;

theBait = 1000;

theSwitch = 0;

ref = [theSwitch, theBait], theBait = ref[0], theSwitch = ref[1];
加载
运行:theBait

但它对于处理返回多个值的函数也很有用。

weatherReport = (location) ->
  # Make an Ajax request to fetch the weather...
  [location, 72, "Mostly Sunny"]

[city, temp, forecast] = weatherReport "Berkeley, CA"
var city, forecast, ref, temp, weatherReport;

weatherReport = function(location) {
  return [location, 72, "Mostly Sunny"];
};

ref = weatherReport("Berkeley, CA"), city = ref[0], temp = ref[1], forecast = ref[2];
加载
运行:forecast

解构赋值可以用于任何深度的数组和对象嵌套,以帮助提取深度嵌套的属性。

futurists =
  sculptor: "Umberto Boccioni"
  painter:  "Vladimir Burliuk"
  poet:
    name:   "F.T. Marinetti"
    address: [
      "Via Roma 42R"
      "Bellagio, Italy 22021"
    ]

{sculptor} = futurists

{poet: {name, address: [street, city]}} = futurists
var city, futurists, name, ref, ref1, sculptor, street;

futurists = {
  sculptor: "Umberto Boccioni",
  painter: "Vladimir Burliuk",
  poet: {
    name: "F.T. Marinetti",
    address: ["Via Roma 42R", "Bellagio, Italy 22021"]
  }
};

sculptor = futurists.sculptor;

ref = futurists.poet, name = ref.name, (ref1 = ref.address, street = ref1[0], city = ref1[1]);
加载
运行:name + "-" + street

解构赋值甚至可以与 splat 结合使用。

tag = "<impossible>"

[open, contents..., close] = tag.split("")
var close, contents, i, open, ref, tag,
  slice = [].slice;

tag = "<impossible>";

ref = tag.split(""), open = ref[0], contents = 3 <= ref.length ? slice.call(ref, 1, i = ref.length - 1) : (i = 1, []), close = ref[i++];
加载
运行:contents.join("")

扩展可以用来从数组末尾检索元素,而无需分配其剩余的值。它在函数参数列表中也有效。

text = "Every literary critic believes he will
        outwit history and have the last word"

[first, ..., last] = text.split " "
var first, last, ref, text;

text = "Every literary critic believes he will outwit history and have the last word";

ref = text.split(" "), first = ref[0], last = ref[ref.length - 1];
加载
运行:first + " " + last

解构赋值在与类构造函数结合使用时也很有用,可以将属性从传递给构造函数的选项对象分配给你的实例。

class Person
  constructor: (options) ->
    {@name, @age, @height = 'average'} = options

tim = new Person name: 'Tim', age: 4
var Person, tim;

Person = (function() {
  function Person(options) {
    var ref;
    this.name = options.name, this.age = options.age, this.height = (ref = options.height) != null ? ref : 'average';
  }

  return Person;

})();

tim = new Person({
  name: 'Tim',
  age: 4
});
加载
运行:tim.age + " " + tim.height

上面的例子也说明了,如果解构的对象或数组中缺少属性,你可以像在 JavaScript 中一样提供默认值。与 JavaScript 的区别在于,CoffeeScript 像往常一样将 null 和 undefined 视为相同。

绑定函数,生成器函数

在 JavaScript 中,this 关键字是动态作用域的,表示当前函数所附加的对象。如果你将函数作为回调传递或将其附加到另一个对象,this 的原始值将丢失。如果你不熟悉这种行为,这篇 Digital Web 文章 对这些怪癖进行了很好的概述。

胖箭头 => 可以用来定义函数,也可以将其绑定到 this 的当前值,就在现场。这在使用基于回调的库(如 Prototype 或 jQuery)时很有用,用于创建迭代器函数以传递给 each,或创建事件处理函数以用于 on。使用胖箭头创建的函数能够访问定义它们的 this 的属性。

Account = (customer, cart) ->
  @customer = customer
  @cart = cart

  $('.shopping_cart').on 'click', (event) =>
    @customer.purchase @cart
var Account;

Account = function(customer, cart) {
  this.customer = customer;
  this.cart = cart;
  return $('.shopping_cart').on('click', (function(_this) {
    return function(event) {
      return _this.customer.purchase(_this.cart);
    };
  })(this));
};
加载

如果我们在上面的回调中使用 ->@customer 将引用 DOM 元素的未定义的“customer”属性,尝试在其上调用 purchase() 将引发异常。

在类定义中使用时,使用胖箭头声明的方法将在实例构造时自动绑定到类的每个实例。

CoffeeScript 函数还通过 yield 关键字支持 ES2015 生成器函数。没有 function*(){} 这种无稽之谈——CoffeeScript 中的生成器只是一个产生值的函数。

perfectSquares = ->
  num = 0
  loop
    num += 1
    yield num * num
  return

window.ps or= perfectSquares()
var perfectSquares;

perfectSquares = function*() {
  var num;
  num = 0;
  while (true) {
    num += 1;
    yield num * num;
  }
};

window.ps || (window.ps = perfectSquares());
加载
运行:ps.next().value

yield* 被称为 yield from,如果你需要强制一个不产生值的生成器,可以使用 yield return

你可以使用 for…from 迭代生成器函数。

fibonacci = ->
  [previous, current] = [1, 1]
  loop
    [previous, current] = [current, previous + current]
    yield current
  return

getFibonacciNumbers = (length) ->
  results = [1]
  for n from fibonacci()
    results.push n
    break if results.length is length
  results
var fibonacci, getFibonacciNumbers;

fibonacci = function*() {
  var current, previous, ref, ref1;
  ref = [1, 1], previous = ref[0], current = ref[1];
  while (true) {
    ref1 = [current, previous + current], previous = ref1[0], current = ref1[1];
    yield current;
  }
};

getFibonacciNumbers = function(length) {
  var n, ref, results;
  results = [1];
  ref = fibonacci();
  for (n of ref) {
    results.push(n);
    if (results.length === length) {
      break;
    }
  }
  return results;
};
加载
运行:getFibonacciNumbers(10)

嵌入式 JavaScript

希望你永远不需要使用它,但如果你需要在你的 CoffeeScript 中插入 JavaScript 代码片段,你可以使用反引号直接传递它。

hi = `function() {
  return [document.title, "Hello JavaScript"].join(": ");
}`
var hi;

hi = function() {
  return [document.title, "Hello JavaScript"].join(": ");
};
加载
运行:hi()

用反斜杠转义反引号:\`​ 变成 `​

在反引号之前用更多反斜杠转义反斜杠:\\\`​ 变成 \`​

markdown = `function () {
  return \`In Markdown, write code like \\\`this\\\`\`;
}`
var markdown;

markdown = function () {
  return `In Markdown, write code like \`this\``;
};
加载
运行:markdown()

你也可以使用三个反引号嵌入 JavaScript 代码块。如果你需要在 JavaScript 代码块中使用反引号,这比转义反引号更容易。

```
function time() {
  return `The time is ${new Date().toLocaleTimeString()}`;
}
```

function time() {
  return `The time is ${new Date().toLocaleTimeString()}`;
}
;

加载
运行:time()

Switch/When/Else

JavaScript 中的 Switch 语句有点笨拙。你需要记住在每个 case 语句的末尾使用 break 来避免意外地落入默认情况。CoffeeScript 可以防止意外落入,并且可以将 switch 转换为可返回的、可赋值的表达式。格式为:switch 条件,when 子句,else 默认情况。

与 Ruby 一样,CoffeeScript 中的 switch 语句可以为每个 when 子句接受多个值。如果任何值匹配,该子句就会运行。

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
switch (day) {
  case "Mon":
    go(work);
    break;
  case "Tue":
    go(relax);
    break;
  case "Thu":
    go(iceFishing);
    break;
  case "Fri":
  case "Sat":
    if (day === bingoDay) {
      go(bingo);
      go(dancing);
    }
    break;
  case "Sun":
    go(church);
    break;
  default:
    go(work);
}
加载

Switch 语句也可以在没有控制表达式的情况下使用,将其转换为 if/else 链的更简洁的替代方案。

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'
var grade, score;

score = 76;

grade = (function() {
  switch (false) {
    case !(score < 60):
      return 'F';
    case !(score < 70):
      return 'D';
    case !(score < 80):
      return 'C';
    case !(score < 90):
      return 'B';
    default:
      return 'A';
  }
})();
加载

Try/Catch/Finally

Try 表达式与 JavaScript 中的 try 语句具有相同的语义,尽管在 CoffeeScript 中,你可以省略两者,即 catch 和 finally 部分。如果不需要错误参数,catch 部分也可以省略错误参数。

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

try {
  allHellBreaksLoose();
  catsAndDogsLivingTogether();
} catch (error1) {
  error = error1;
  print(error);
} finally {
  cleanUp();
}
加载

链式比较

CoffeeScript 从 Python 中借鉴了 链式比较——使测试值是否在某个范围内变得容易。

cholesterol = 127

healthy = 200 > cholesterol > 60
var cholesterol, healthy;

cholesterol = 127;

healthy = (200 > cholesterol && cholesterol > 60);
加载
运行:healthy

字符串插值,块字符串和块注释

Ruby 风格的字符串插值包含在 CoffeeScript 中。双引号字符串允许插值,使用 #{ … },单引号字符串是字面量。你甚至可以在对象键中使用插值。

author = "Wittgenstein"
quote  = "A picture is a fact. -- #{ author }"

sentence = "#{ 22 / 7 } is a decent approximation of π"
var author, quote, sentence;

author = "Wittgenstein";

quote = "A picture is a fact. -- " + author;

sentence = (22 / 7) + " is a decent approximation of π";
加载
运行:sentence

CoffeeScript 允许使用多行字符串。除非行以反斜杠结尾,否则行之间用单个空格连接。缩进被忽略。

mobyDick = "Call me Ishmael. Some years ago --
  never mind how long precisely -- having little
  or no money in my purse, and nothing particular
  to interest me on shore, I thought I would sail
  about a little and see the watery part of the
  world..."
var mobyDick;

mobyDick = "Call me Ishmael. Some years ago -- never mind how long precisely -- having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world...";
加载
运行:mobyDick

块字符串可以用来保存格式化或缩进敏感的文本(或者,如果你不想转义引号和撇号)。开始块的缩进级别在整个块中保持一致,因此你可以将所有内容与代码主体对齐。

html = """
       <strong>
         cup of coffeescript
       </strong>
       """
var html;

html = "<strong>\n  cup of coffeescript\n</strong>";
加载
运行:html

双引号块字符串,与其他双引号字符串一样,允许插值。

有时你可能希望将块注释传递到生成的 JavaScript 中。例如,当你需要在文件顶部嵌入许可证头时。块注释,其语法与块字符串相同,在生成的代码中被保留。

###
SkinnyMochaHalfCaffScript Compiler v1.0
Released under the MIT License
###

/*
SkinnyMochaHalfCaffScript Compiler v1.0
Released under the MIT License
 */

加载

标记模板字面量

CoffeeScript 支持 ES2015 标记模板字面量,它允许自定义字符串插值。如果你在字符串前立即加上一个函数名(两者之间没有空格),CoffeeScript 会将这个“函数加字符串”组合输出为 ES2015 标记模板字面量,它将 按预期工作:函数被调用,参数是构成插值字符串的输入文本和表达式部分。然后,函数可以将这些部分组装成一个输出字符串,提供自定义字符串插值。

请注意,CoffeeScript 编译器为此功能输出的是 ES2015 语法,因此你的目标 JavaScript 运行时必须支持此语法才能使你的代码正常工作;或者你可以使用 BabelTraceur 编译器 等工具将此 ES2015 语法转换为兼容的 JavaScript。

upperCaseExpr = (textParts, expressions...) ->
  textParts.reduce (text, textPart, i) ->
    text + expressions[i - 1].toUpperCase() + textPart

greet = (name, adjective) ->
  upperCaseExpr"""
               Hi #{name}. You look #{adjective}!
               """
var greet, upperCaseExpr,
  slice = [].slice;

upperCaseExpr = function() {
  var expressions, textParts;
  textParts = arguments[0], expressions = 2 <= arguments.length ? slice.call(arguments, 1) : [];
  return textParts.reduce(function(text, textPart, i) {
    return text + expressions[i - 1].toUpperCase() + textPart;
  });
};

greet = function(name, adjective) {
  return upperCaseExpr`Hi ${name}. You look ${adjective}!`;
};
加载
运行:greet("greg", "awesome")

块正则表达式

与块字符串和注释类似,CoffeeScript 支持块正则表达式——扩展的正则表达式,它忽略内部空格,并且可以包含注释和插值。模仿 Perl 的 /x 修饰符,CoffeeScript 的块正则表达式由 /// 分隔,并且在很大程度上使复杂的正则表达式变得可读。引用 CoffeeScript 源代码

OPERATOR = /// ^ (
  ?: [-=]>             # function
   | [-+*/%<>&|^!?=]=  # compound assign / compare
   | >>>=?             # zero-fill right shift
   | ([-+:])\1         # doubles
   | ([&|<>])\2=?      # logic / shift
   | \?\.              # soak access
   | \.{2,3}           # range or splat
) ///
var OPERATOR;

OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/;
加载

模块

ES2015 模块在 CoffeeScript 中得到支持,具有非常相似的 importexport 语法

import 'local-file.coffee'
import 'coffeescript'

import _ from 'underscore'
import * as underscore from 'underscore'

import { now } from 'underscore'
import { now as currentTimestamp } from 'underscore'
import { first, last } from 'underscore'
import utilityBelt, { each } from 'underscore'

export default Math
export square = (x) -> x * x
export class Mathematics
  least: (x, y) -> if x < y then x else y

export { sqrt }
export { sqrt as squareRoot }
export { Mathematics as default, sqrt as squareRoot }

export * from 'underscore'
export { max, min } from 'underscore'
import 'local-file.coffee';

import 'coffeescript';

import _ from 'underscore';

import * as underscore from 'underscore';

import {
  now
} from 'underscore';

import {
  now as currentTimestamp
} from 'underscore';

import {
  first,
  last
} from 'underscore';

import utilityBelt, {
  each
} from 'underscore';

export default Math;

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

export var Mathematics = (function() {
  function Mathematics() {}

  Mathematics.prototype.least = function(x, y) {
    if (x < y) {
      return x;
    } else {
      return y;
    }
  };

  return Mathematics;

})();

export {
  sqrt
};

export {
  sqrt as squareRoot
};

export {
  Mathematics as default,
  sqrt as squareRoot
};

export * from 'underscore';

export {
  max,
  min
} from 'underscore';
加载

请注意,CoffeeScript 编译器不解析模块;在 CoffeeScript 中编写 importexport 语句将生成 importexport 语句,作为最终的输出。你需要附加另一个转译器,例如 Traceur 编译器BabelRollup,将此 ES2015 语法转换为在目标运行时中工作的代码。

还要注意,任何包含 importexport 语句的文件都将输出,不会包含 顶层函数安全包装器;换句话说,导入或导出模块将自动为该文件触发 模式。这是因为根据 ES2015 规范,importexport 语句必须出现在最顶层的范围。

Cake 和 Cakefiles

CoffeeScript 包含一个(非常)简单的构建系统,类似于 MakeRake。当然,它被称为 Cake,用于构建和测试 CoffeeScript 语言本身的任务。任务在名为 Cakefile 的文件中定义,可以通过在目录中运行 cake [task] 来调用。要打印所有任务和选项的列表,只需键入 cake

任务定义是用 CoffeeScript 编写的,因此你可以在你的 Cakefile 中放置任意代码。使用名称、长描述和在运行任务时调用的函数来定义任务。如果你的任务需要命令行选项,你可以使用短标志和长标志来定义选项,它将在 options 对象中可用。以下是一个使用 Node.js API 重新构建 CoffeeScript 解析器的任务

fs = require 'fs'

option '-o', '--output [DIR]', 'directory for compiled code'

task 'build:parser', 'rebuild the Jison parser', (options) ->
  require 'jison'
  code = require('./lib/grammar').parser.generate()
  dir  = options.output or 'lib'
  fs.writeFile "#{dir}/parser.js", code
var fs;

fs = require('fs');

option('-o', '--output [DIR]', 'directory for compiled code');

task('build:parser', 'rebuild the Jison parser', function(options) {
  var code, dir;
  require('jison');
  code = require('./lib/grammar').parser.generate();
  dir = options.output || 'lib';
  return fs.writeFile(dir + "/parser.js", code);
});
加载

如果你需要在另一个任务之前调用一个任务——例如,在 test 之前运行 build,你可以使用 invoke 函数:invoke 'build'。Cake 任务是将你的 CoffeeScript 函数暴露给命令行的最小方法,因此 不要期望任何内置的奇特功能。如果你需要依赖项或异步回调,最好将它们放在你的代码本身中——而不是 cake 任务中。

源映射

CoffeeScript 1.6.1 及更高版本包含对生成源映射的支持,这是一种告诉你的 JavaScript 引擎你的 CoffeeScript 程序的哪一部分与正在评估的代码相匹配的方法。支持它的浏览器可以自动使用源映射在调试器中显示你的原始源代码。要在你的 JavaScript 文件旁边生成源映射,请向编译器传递 --map-m 标志。

有关源映射的完整介绍,包括其工作原理以及如何在浏览器中连接它们,请阅读HTML5 教程

“text/coffeescript” 脚本标签

虽然不建议用于严肃用途,但 CoffeeScript 可以使用<script type="text/coffeescript">标签直接包含在浏览器中。源代码包含编译器的压缩和缩小版本(在此处下载当前版本,gzip 后为 51k)作为docs/v1/browser-compiler/coffee-script.js。在包含内联 CoffeeScript 标签的页面上包含此文件,它将按顺序编译和评估它们。

事实上,上面运行“尝试 CoffeeScript”的少量粘合脚本,以及菜单的 jQuery,就是以这种方式实现的。查看源代码并查看页面底部以查看示例。包含脚本还允许您访问CoffeeScript.compile(),因此您可以打开 Firebug 并尝试编译一些字符串。

关于 CoffeeScript 的通常注意事项适用 - 您的内联脚本将在闭包包装器中运行,因此如果您想公开全局变量或函数,请将它们附加到window对象。

书籍

有很多优秀的资源可以帮助您开始使用 CoffeeScript,其中一些资源可以在线免费获取。

屏幕录像

示例

可以在 GitHub 上找到 最佳的开源 CoffeeScript 示例列表。但为了提供更多示例,

资源

网络聊天(IRC)

通常可以在 CoffeeScript IRC 频道中找到快速帮助和建议。加入#coffeescript on irc.freenode.net,或单击下面的按钮以在此页面上打开网络聊天会话。

更改日志

1.12.7

1.12.6

1.12.5

1.12.4

1.12.3

1.12.2

1.12.1

1.12.0

1.11.1

1.11.0

1.10.0

1.9.3

1.9.2

1.9.1

1.9.0

1.8.0

1.7.1

1.7.0

$ 'body'
.click (e) ->
  $ '.box'
  .fadeIn 'fast'
  .addClass '.active'
.css 'background', 'white'
$('body').click(function(e) {
  return $('.box').fadeIn('fast').addClass('.active');
}).css('background', 'white');
加载

1.6.3

1.6.2

1.6.1

1.5.0

1.4.0

1.3.3

1.3.1

1.2.0

1.1.3

1.1.2

修复了块注释格式、`?=`编译、对控制结构的隐式调用、try/catch 块的隐式调用、可变参数从局部作用域泄漏、heregexes 之后的语法错误中的行号、对括号内的数字字面量的属性访问、绑定类方法和带有保留名称的 super、REPL 大修、连续编译的分号、隐式调用对象中的块注释以及 Chrome 错误。

1.1.1

用于具有外部构造函数的类的错误修复版本,请参见问题 #1182。

1.1.0

通过`coffee`可执行文件运行时,`process.argv`及其相关项现在报告`coffee`而不是`node`。与**Node.js 0.4.x**模块查找更改的更好的兼容性。REPL 中的输出现在是彩色化的,就像 Node 的一样。使用`--join`时,现在必须为你的连接的 CoffeeScript 指定一个名称。修复了意外地将复合除法`/=`解析为正则表达式的错误。现在,所有`text/coffeescript`标签都应该按包含顺序执行。修复了使用外部构造函数的扩展子类的问题。修复了`addImplicitParentheses`中的边缘情况无限循环。修复了长函数调用链的指数级减速。全局变量不再泄漏到 CoffeeScript REPL 中。splatted 参数被声明为函数的局部变量。

1.0.1

修复了 Unicode 标识符的词法分析器错误。更新了 REPL 以与 Node.js 0.3.7 兼容。修复了在 REPL 中要求相对路径的问题。尾随`return`和`return undefined`现在被优化掉了。停止要求核心 Node.js`util`模块以向后兼容 Node.js 0.2.5。修复了条件`return`会导致`switch`语句中出现穿透的情况。优化了解构赋值中的空对象。

1.0.0

CoffeeScript 循环不再尝试在循环体中生成函数时保留块作用域。相反,你可以使用`do`关键字来创建一个方便的闭包包装器。添加了一个`--nodejs`标志,用于将选项直接传递给`node`可执行文件。在表达式中使用纯语句时,行为更好。修复了所有浏览器的`-1`的包含切片,以及使用任意表达式作为端点的拼接。

0.9.6

REPL 现在正确地格式化堆栈跟踪,并在异步异常时保持活动状态。使用`--watch`现在会在编译文件时打印时间戳。修复了一些意外泄漏到提取的闭包循环中的变量。构造函数现在在类体中保留其声明位置。动态对象键已被移除。现在支持嵌套类。修复了裸 splatted 函数的执行上下文。修复了链式比较的倒置。链式类实例化现在可以与 splat 正确地一起使用。

0.9.5

0.9.5 应该被认为是 CoffeeScript 1.0 的第一个候选版本。自上一个版本以来,已经进行了大量的内部更改,其中许多是由**satyr**的 Coco CoffeeScript 方言贡献的。添加了 heregexes(扩展正则表达式)。函数现在可以具有默认参数。类体现在是可执行代码。改进了无效 CoffeeScript 的语法错误。`undefined`现在像`null`一样工作,并且不能被分配新值。关于单行推导式,存在优先级更改:`result = i for i in list` 以前默认解析为`result = (i for i in list)`……现在解析为`(result = i) for i in list`。

0.9.4

CoffeeScript 现在使用适当命名的临时变量,并在使用后回收其引用。添加了对**Node.js 0.3**的`require.extensions`支持。在浏览器中加载 CoffeeScript 现在只向全局作用域添加一个`CoffeeScript`对象。修复了隐式对象和块注释边缘情况。

0.9.3

CoffeeScript`switch`语句现在编译成 JS`switch`语句——它们以前编译成`if/else`链以兼容 JavaScript 1.3。现在支持浸泡函数调用。RubyMine 编辑器的用户现在应该能够使用`--watch`模式。

0.9.2

现在可以省略指定范围字面量的开始和结束,例如`array[3..]`。你现在可以写`a not instanceof b`。修复了与嵌套的显着和非显着缩进相关的重大错误(问题 #637)。添加了一个`--require`标志,允许你连接到`coffee`命令。添加了一个自定义的`jsl.conf`文件,用于我们首选的 JavaScriptLint 设置。通过为操作扁平化规则,加快了 Jison 语法编译时间。块注释现在可以使用与 JavaScript-minifier 友好的语法。添加了 JavaScript 的复合赋值按位运算符。修复了具有前导数字和字符串键的隐式对象字面量、作为隐式调用的主题以及作为复合赋值的一部分的错误。

0.9.1

用于**0.9.1**的错误修复版本。极大地改进了对混合隐式对象、隐式函数调用和隐式缩进的处理。字符串和正则表达式插值现在严格为`#{ … }`(Ruby 风格)。编译器现在接受一个`--require`标志,该标志指定在编译之前运行的脚本。

0.9.0

CoffeeScript **0.9** 系列被认为是 **1.0** 的候选版本;让我们给她来一次试航。**0.9.0** 引入了一个重大的向后不兼容的更改:赋值现在使用 `=`,对象字面量使用 `:`,就像在 JavaScript 中一样。这使我们能够拥有隐式对象字面量和 YAML 风格的对象定义。半赋值被移除,取而代之的是 `+=`、`or=` 等。插值现在使用井号 `#` 而不是美元符号 `$` - 因为美元符号可能是有效的 JS 标识符的一部分。向下范围推导现在再次安全,并且在使用整数端点创建时被优化为直接的 for 循环。添加了一种快速、无保护的对象推导形式:`for all key, value of object`。在没有参数的情况下提及 `super` 关键字现在会转发传递给函数的所有参数,就像在 Ruby 中一样。如果你从父类 `A` 扩展类 `B`,如果 `A` 定义了 `extended` 方法,它将被调用,并将 `B` 传递进去 - 这在其他方面实现了静态继承。使用胖箭头绑定的函数的更清晰的输出。`@variables` 现在可以在参数列表中使用,参数将自动设置为对象上的属性 - 这在构造函数和 setter 函数中很有用。构造函数现在可以接受 splat。

0.7.2

快速修复 bug(紧随 0.7.1 之后),解决了一个问题,该问题阻止了 `coffee` 命令行选项在某些情况下被解析。

0.7.1

块级注释现在被传递并打印为 JavaScript 块级注释 - 使它们对许可证和版权页眉有用。更好地支持通过 hashbang 独立运行 coffee 脚本。改进了对不在语法中的标记的语法错误。

0.7.0

CoffeeScript 变量风格现在是 camelCase,就像在 JavaScript 中一样。保留字现在允许作为对象键,并将为你引用。范围推导现在生成更干净的代码,但如果你想向下迭代,则必须指定 `by -1`。与之前的版本相比,语法错误的报告得到了极大的改进。在没有参数的情况下运行 `coffee` 现在会启动 REPL,并支持 Readline。`<-` 绑定运算符已从 CoffeeScript 中移除。添加了 `loop` 关键字,它等效于 `while true` 循环。包含闭包的推导现在将像 `forEach` 的语义一样闭包其变量。你现在可以在类定义中使用绑定函数(绑定到实例)。为了保持一致性,`a in b` 现在是数组存在检查,`a of b` 是对象键检查。注释不再传递到生成的 JavaScript 中。

0.6.2

`coffee` 命令现在将在编译一个充满脚本的目录时保留目录结构。修复了两个遗漏,这些遗漏阻止了 CoffeeScript 编译器在 Internet Explorer 中实时运行。现在有一个块注释语法,类似于 CoffeeScript 的 heredocs。ECMA Harmony DRY 风格的模式匹配现在受支持,其中属性的名称与值的名称相同:`{name, length}: func`。模式匹配现在允许在推导变量中使用。`unless` 现在允许在块形式中使用。添加了 `until` 循环,作为 `while` 循环的逆运算。`switch` 语句现在允许不使用 switch 对象子句。与 Node.js **v0.1.95** 兼容。

0.6.1

升级 CoffeeScript 以与新的 Node.js **v0.1.90** 系列兼容。

0.6.0

现在允许尾随逗号,类似于 Python。静态属性可以使用 `@property` 符号直接在类定义中赋值。

0.5.6

插值现在可以在正则表达式和 heredocs 以及字符串中使用。添加了 `<-` 绑定运算符。允许赋值给半表达式,而不是特殊的 `||=` 风格的运算符。arguments 对象不再自动转换为数组。在要求 `coffee-script` 之后,Node.js 现在可以直接加载 `.coffee` 文件,这要归功于 **registerExtension**。现在可以在函数调用、数组和模式匹配中使用多个 splat。

0.5.5

字符串插值,由 Stan Angeloff 贡献。由于 `--run` 自 **0.5.3** 以来一直是默认值,因此更新 `--stdio` 和 `--eval` 以默认运行,如果你想打印结果,也传递 `--compile`。

0.5.4

修复了纠正 Node.js 全局常量 `__filename` 和 `__dirname` 的 bug。调整以更灵活地解析嵌套函数字面量和格式不正确的缩进注释。更新了最新的 Node.js API。

0.5.3

CoffeeScript 现在有一个定义类的语法。许多核心组件(节点、词法分析器、重写器、作用域、Optparse)都在使用它们。Cakefiles 可以使用 `optparse.coffee` 来定义任务的选项。`--run` 现在是 `coffee` 命令的默认标志,使用 `--compile` 保存 JavaScripts。修复了 RegExp 字面量和链式除法之间的歧义的 bug。

0.5.2

添加了编译器的压缩版本,以便包含在网页中,作为 `browser-compiler/coffee-script.js`。它会自动运行任何类型为 `text/coffeescript` 的脚本标签。在 `coffee` 命令中添加了 `--stdio` 选项,用于管道输入编译。

0.5.1

改进了使用存在运算符的空值浸泡,包括对索引属性的浸泡。在 `while` 循环中添加了条件,因此你可以像推导一样使用 `when` 将它们用作过滤器。

0.5.0

CoffeeScript 0.5.0 是一个主要版本,虽然没有语言更改,但 Ruby 编译器已被移除,取而代之的是用纯 CoffeeScript 编写的自托管编译器。

0.3.2

`@property` 现在是 `this.property` 的简写。将默认的 JavaScript 引擎从 Narwhal 切换到 Node.js。如果你想继续使用它,请传递 `--narwhal` 标志。

0.3.0

CoffeeScript 0.3 包含主要的语法更改:函数符号已更改为 `->`,绑定函数符号现在是 `=>`。函数定义中的参数列表现在必须用括号括起来。添加了属性浸泡,使用 `?.` 运算符。在使用参数调用函数时,使括号可选。删除了过时的块字面量语法。

0.2.6

添加了 Python 风格的链式比较,条件存在运算符 `?=`,以及来自 *Beautiful Code* 的一些示例。与语句到表达式的转换、参数到数组的转换以及 TextMate 语法高亮相关的 bug 修复。

0.2.5

switch 语句中的条件现在可以一次接受多个值 - 如果其中任何一个为真,则 case 将运行。添加了长箭头 `==>`,它定义并立即将函数绑定到 `this`。while 循环现在可以像推导一样用作表达式。splat 可以用在模式匹配中,以吸收数组的剩余部分。

0.2.4

添加了 ECMAScript Harmony 风格的解构赋值,用于处理从嵌套数组和对象中提取值。添加了缩进敏感的 heredocs,用于格式良好的字符串或代码块。

0.2.3

删除了不令人满意的 `ino` 关键字,用 `of` 替换它,用于对象推导。它们现在看起来像:`for prop, value of object`。

0.2.2

在对对象执行推导时,使用 `ino` 而不是 `in`,这有助于我们在编译时生成更小、更高效的代码。添加了 `::` 作为 `prototype.` 的简写。“splat” 符号已从前缀星号 `*` 更改为后缀省略号 `...`。添加了 JavaScript 的 `in` 运算符、空 `return` 语句和空 `while` 循环。以大写字母开头的构造函数现在包含一个安全检查,以确保返回对象的新的实例。`extends` 关键字现在与 Google 的 Closure 库中的 `goog.inherits` 功能相同。

0.2.1

arguments 对象现在在引用时被转换为真正的数组。

0.2.0

主要版本。显著的空白。更好的语句到表达式的转换。splat。splice 字面量。对象推导。块。存在运算符。感谢所有发布问题的人,特别感谢 Liam O’Connor-Davis 提供的空白和表达式帮助。

0.1.6

修复了从 CoffeeScript 目录之外运行 `coffee --interactive` 和 `--run` 的 bug。修复了嵌套函数/if 语句的 bug。

0.1.5

数组切片字面量和数组推导现在都可以使用 Ruby 风格的范围来指定开始和结束。JavaScript 变量声明现在被推到作用域的顶部,使所有赋值语句成为表达式。你可以使用 `\` 来转义换行符。`coffee-script` 命令现在称为 `coffee`。

0.1.4

CoffeeScript 的官方扩展现在是 `.coffee` 而不是 `.cs`,`.cs` 属于 C#。由于用户的强烈要求,你现在也可以使用 `=` 来赋值。与 JavaScript 不同,`=` 也可以在对象字面量中使用,与 `:` 交换使用。对像 `func(1)(2)(3)(4)` 这样的链式函数调用的语法进行了修正。继承和 super 不再使用 `__proto__`,因此它们现在应该与 IE 兼容。

0.1.3

coffee 命令现在包含 --interactive,它启动一个交互式的 CoffeeScript 会话,以及 --run,它直接编译并执行脚本。这两个选项都依赖于 Narwhal 的正常安装。aint 关键字已被 isnt 替换,它与 is 更为协调。引号字符串现在允许作为对象字面量中的标识符:例如 {"5+5": 10}。所有赋值运算符现在都使用冒号:+:-:*: 等。

0.1.2

修复了通过多个继承级别调用 super() 的错误,并重新添加了 extends 关键字。添加了对实验性 Narwhal 的支持(作为 Tusk 包),由 Tom Robinson 贡献,包括 bin/cs 作为 CoffeeScript REPL 和解释器。新的 --no-wrap 选项用于抑制安全函数包装器。

0.1.1

添加了 instanceoftypeof 作为运算符。

0.1.0

CoffeeScript 首次发布。