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
/await
、JSX、物件 rest/spread 語法,以及 使用現代語法產生的 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;
})();
coffee
的命令列版本可用作 Node.js 工具程式。然而,核心編譯器 並不依賴於 Node,可以在任何 JavaScript 環境或瀏覽器中執行(請參閱 試用 CoffeeScript)。
要安裝,首先請確定您擁有 Node.js 最新穩定版本的有效副本。然後,您可以使用 npm 全域安裝 CoffeeScript
npm install --global coffeescript
這將使 coffee
和 cake
命令可於全域使用。
當您需要 CoffeeScript 作為專案的相依項時,您可以在該專案的資料夾中進行本機安裝
npm install --save coffeescript
coffee
和 cake
命令會先在目前資料夾中尋找 CoffeeScript 是否已在本機安裝,如果已安裝,則使用該版本。這允許在全域和本機安裝不同版本的 CoffeeScript。
安裝後,您應該可以存取 coffee
命令,它可以執行腳本、將 .coffee
檔案編譯成 .js
,並提供互動式 REPL。coffee
命令採用下列選項
選項 | 說明 |
---|---|
-c, --compile |
將 .coffee 程式碼編譯成同名的 .js JavaScript 檔案。 |
-m, --map |
在編譯的 JavaScript 檔案旁產生原始碼對應檔。也會將 sourceMappingURL 指令加入 JavaScript 中。 |
-M, --inline-map |
與 --map 相同,但將原始碼對應檔直接包含在編譯的 JavaScript 檔案中,而不是在獨立的檔案中。 |
-i, --interactive |
啟動互動式 CoffeeScript 會話,以嘗試簡短的片段。與不帶參數呼叫 coffee 相同。 |
-o, --output [DIR] |
將所有編譯的 JavaScript 檔案寫入指定的目錄中。搭配 --compile 或 --watch 使用。 |
-w, --watch |
監控檔案的變更,在任何檔案更新時重新執行指定的指令。 |
-p, --print |
不將 JavaScript 寫入檔案,而是直接列印到 stdout。 |
-s, --stdio |
透過 STDIN 輸入 CoffeeScript,並透過 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 進行詞法分析,而不進行解析,並列印出 token 串流。用於除錯編譯器。 |
-n, --nodes |
僅對 CoffeeScript 進行詞法分析和解析,而不進行編譯,並列印出解析樹。用於除錯編譯器。 |
--nodejs |
node 可執行檔具有一些有用的選項,例如 --debug 、--debug-brk 、--max-stack-size 和 --expose-gc 。使用此標誌將選項直接傳遞至 Node.js。若要傳遞多個標誌,請多次使用 --nodejs 。 |
--no-header |
抑制「由 CoffeeScript 生成」標頭。 |
src
中的 .coffee
檔案目錄樹編譯成 lib
中的 .js
檔案平行樹coffee --compile --output lib/ src/
coffee --watch --compile experimental.coffee
coffee --join project.js --compile src/*.coffee
coffee -bpe "alert i for i in [0..10]"
coffee -o lib/ -cw src/
Ctrl-D
退出,Ctrl-V
進行多行)咖啡
除了作為一種普通的程式語言使用之外,CoffeeScript 也可以用「可讀」模式撰寫。如果您使用 .litcoffee
副檔名命名檔案,就可以將其寫成 Markdown 文件,而這個文件同時也是可執行的 CoffeeScript 程式碼。編譯器會將任何縮排區塊(Markdown 表示原始程式碼的方式)視為程式碼,並將其餘部分視為註解而忽略。
僅供參考,目前編譯器的一小部分是以這種方式實作的:請參閱 文件、原始碼,以及 在文字編輯器中正確突顯的程式碼。
此參考文件採用由上而下的結構,方便您閱讀。後面的章節會使用前面介紹的概念和語法。假設您已熟悉 JavaScript。在以下所有範例中,CoffeeScript 原始碼會顯示在左側,而直接編譯成 JavaScript 的程式碼則會顯示在右側。
許多範例都可以執行(在有意義的情況下),方法是按一下右側的執行按鈕,並可透過按一下左側的載入按鈕載入「試用 CoffeeScript」主控台。
首先,基本知識:CoffeeScript 使用顯著空白來界定程式碼區塊。您不需要使用分號 ;
來終止表達式,結束該行就足夠了(儘管分號仍然可用於將多個表達式放入單一行中)。不要使用大括號 { }
來包圍 函式、if 陳述式、switch 和 try/catch 中的程式碼區塊,請使用縮排。
如果您傳遞引數,則不需要使用括號來呼叫函式。隱式呼叫會向前包覆到該行或區塊表達式的結尾。
console.log sys.inspect object
→ console.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;
};
函式也可以為引數設定預設值,如果沒有傳入引數(null
或 undefined
),則會使用預設值。
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 + "...";
};
物件和陣列的 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
}
};
在 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();
請注意,所有變數宣告都會向上推到最近的範圍頂端,在它們第一次出現時。outer 沒有在內部函式中重新宣告,因為它已經在範圍內;另一方面,函式內的 inner 不應該能夠變更同名的外部變數值,因此有它自己的宣告。
此行為實際上與 Ruby 的局部變數範圍相同。因為您無法直接存取 var
關鍵字,因此不可能故意遮蔽外部變數,您只能參照它。因此,如果您撰寫深度巢狀函式,請小心不要意外重複使用外部變數的名稱。
雖然為了清楚起見,在本文檔中已隱藏,但所有 CoffeeScript 輸出都包裝在一個匿名函式中:(function(){ … })();
這個安全包裝器與 var
關鍵字的自動產生相結合,使得意外污染全域名稱空間變得極為困難。
如果您想為其他腳本建立頂層變數以供使用,請將它們附加為 window 上的屬性;將它們附加為 CommonJS 中 exports 物件上的屬性;或使用 export
陳述式。如果您同時鎖定 CommonJS 和瀏覽器,存在運算子(如下所述)提供了一個可靠的方法來找出在哪裡新增它們:exports ? this
if/else 敘述可以不用括號和花括號來寫。和函式及其他區塊表達式一樣,多行條件式以縮排來區分。還有一個方便的後置形式,if
或 unless
在結尾。
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/forEach、map 或 select/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;
})();
請注意,由於我們在上面的範例中將理解的結果指派給變數,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;
})();
如果您只想反覆運算物件本身定義的鍵,請加上 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;
})();
為了可讀性,until 關鍵字等於 while not
,而 loop 關鍵字等於 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);
相同的語法可以用於指定,以新的數值取代陣列的區段,並進行拼接。
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;
請注意,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";
即使函式總是會傳回其最終值,但在你知道已完成時,可以且建議從函式主體中提早傳回,寫出明確的傳回(return value
)。
因為變數宣告發生在範圍的頂端,所以指派可以用在表達式中,即使對於之前未曾見過的變數也是如此
six = (one = 1) + (two = 2) + (three = 3)
var one, six, three, two;
six = (one = 1) + (two = 2) + (three = 3);
在 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);
以及執行愚蠢的操作,例如將 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 中有少數陳述式無法有意義地轉換成表達式,即 break
、continue
和 return
。如果你在程式碼區塊中使用它們,CoffeeScript 就不會嘗試執行轉換。
因為 ==
營運子經常導致不必要的強制轉換、非遞移,且在其他語言中具有不同的意義,CoffeeScript 將 ==
編譯成 ===
,而 !=
編譯成 !==
。此外,is
編譯成 ===
,而 isnt
編譯成 !==
。
你可以使用 not
作為 !
的別名。
對於邏輯,and
編譯成 &&
,而 or
編譯成 ||
。
then
可以用來區分條件和表達式,而不是換行符或分號,在 while、if/else 和 switch/when 陳述中。
如同 YAML 中,on
和 yes
與布林值 true
相同,而 off
和 no
與布林值 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 |
!== |
非 |
! |
與 |
&& |
或 |
|| |
true 、yes 、on |
真 |
false 、no 、off |
假 |
@ 、this |
此 |
的 |
在 |
在 |
無 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,除非變數為 null 或 undefined,這使其類似於 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";
存在運算子的存取器變體 ?.
可用於吸收屬性鏈中的 null 參考。在基底值可能是 null 或 undefined 的情況下,使用它來取代點存取器 .
。如果所有屬性都存在,則您將獲得預期的結果,如果鏈斷裂,則會傳回 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;
吸收 null 類似於 Ruby 的 andand gem,以及 Groovy 中的 安全導覽運算子。
JavaScript 的原型繼承一直有點令人費解,有一整套提供更簡潔語法,用於 JavaScript 原型上古典繼承的函式庫家族:Base2、Prototype.js、JS.Class 等。這些函式庫提供語法糖,但內建繼承會完全可用,如果不是因為幾個小例外:呼叫 super(原型物件對目前函式的實作)很奇怪,而且正確設定原型鏈很奇怪。
CoffeeScript 提供一個基本的 class
結構,讓您可以命名類別、設定超類別、指定原型屬性,以及在單一可指定表達式中定義建構函式,而不是重複將函式附加到原型。
建構函式函式有命名,以更好地支援有用的堆疊追蹤。在以下範例的第一個類別中,this.constructor.name 是 "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, "-");
};
最後,類別定義是可執行程式碼區塊,這會產生有趣的元程式設計可能性。因為在類別定義的脈絡中,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];
但它也有助於處理會傳回多個值的函數。
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];
解構賦值可用於任何深度陣列和物件巢狀結構,以協助取出深度巢狀屬性。
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]);
解構賦值甚至可以與展開結合使用。
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++];
展開可用於從陣列的尾端擷取元素,而不需要指定其餘的值。它也適用於函式參數清單。
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];
解構賦值與類別建構函式結合使用時也很有用,可將屬性從傳遞給建構函式的選項物件指定給您的執行個體。
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
});
上述範例也示範了,如果解構物件或陣列中缺少屬性,您可以像在 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 函式也支援 ES2015 產生器函式,透過 yield
關鍵字。沒有 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());
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;
};
希望你永遠不需要使用它,但如果你需要在 CoffeeScript 中穿插 JavaScript 片段,你可以使用反引號直接傳遞。
hi = `function() {
return [document.title, "Hello JavaScript"].join(": ");
}`
var hi;
hi = function() {
return [document.title, "Hello JavaScript"].join(": ");
};
使用反斜線來跳脫反引號:\`
變成 `
。
使用更多反斜線來跳脫反引號前的反斜線:\\\`
變成 \`
。
markdown = `function () {
return \`In Markdown, write code like \\\`this\\\`\`;
}`
var markdown;
markdown = function () {
return `In Markdown, write code like \`this\``;
};
你也可以使用三個反引號來內嵌 JavaScript 程式區塊。如果你需要在 JavaScript 程式區塊中使用反引號,這比跳脫反引號容易。
```
function time() {
return `The time is ${new Date().toLocaleTimeString()}`;
}
```
function time() {
return `The time is ${new Date().toLocaleTimeString()}`;
}
;
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 表達式具有與 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);
CoffeeScript 包含 Ruby 風格的字串內插。雙引號字串允許內插值,使用 #{ … }
,而單引號字串是字面意思。你甚至可以在物件鍵中使用內插。
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 π";
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...";
區塊字串可用於容納格式化或對縮排敏感的文字(或者,如果你只是不想跳脫引號和撇號)。開始區塊的縮排層級會在整個區塊中維持,因此你可以讓它與程式碼主體保持對齊。
html = """
<strong>
cup of coffeescript
</strong>
"""
var html;
html = "<strong>\n cup of coffeescript\n</strong>";
雙引號區塊字串,就像其他雙引號字串一樣,允許內插。
有時你會希望將區塊註解傳遞到產生的 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 執行環境必須支援這個語法才能讓你的程式碼運作;或者,你可以使用像 Babel 或 Traceur Compiler 的工具將這個 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}!`;
};
類似於區塊字串和註解,CoffeeScript 支援區塊正規表示式 — 忽略內部空白字元且可以包含註解和內插的延伸正規表示式。CoffeeScript 的區塊正規表示式仿效 Perl 的 /x
修飾詞,以 ///
為分隔符號,有助於讓複雜的正規表示式變得更易於閱讀。引用 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})/;
CoffeeScript 支援 ES2015 模組,其 import
和 export
語法非常類似
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 中撰寫 import
或 export
陳述式會在結果輸出中產生 import
或 export
陳述式。您有責任附加另一個轉譯器,例如 Traceur Compiler、Babel 或 Rollup,以將此 ES2015 語法轉換為可在您的目標執行時期運作的程式碼。
另外請注意,任何含有 import
或 export
陳述式的檔案都將在沒有 頂層函式安全包裝器 的情況下輸出;換句話說,匯入或匯出模組會自動觸發該檔案的 bare 模式。這是因為根據 ES2015 規範,import
或 export
陳述式必須出現在最頂層範圍。
CoffeeScript 包含一個(非常)簡單的建置系統,類似於 Make 和 Rake。它當然稱為 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 教學。
雖然不建議用於嚴肅用途,但可以使用 <script type="text/coffeescript">
標籤直接在瀏覽器中包含 CoffeeScript。來源包含編譯器的壓縮和縮小版本(在此處下載當前版本,gzip 壓縮後為 51k),作為 docs/v1/browser-compiler/coffee-script.js
。在包含內嵌 CoffeeScript 標籤的頁面上包含此檔案,它將依序編譯和評估這些標籤。
事實上,執行上方「試用 CoffeeScript」的少量膠水腳本,以及選單的 jQuery,就是以這種方式實作的。檢視來源並查看頁面底部以查看範例。包含腳本也會讓您可以存取 CoffeeScript.compile()
,因此您可以開啟 Firebug 並嘗試編譯一些字串。
CoffeeScript 適用於一般警告 — 您的內嵌腳本將在封閉包裝中執行,因此如果您想要公開全域變數或函式,請將它們附加到 window
物件。
有許多優良的資源可以協助您開始使用 CoffeeScript,其中一些可以在線上免費取得。
可以在 GitHub 上找到 最佳開源 CoffeeScript 範例清單。但再列舉幾個
原始碼
使用 bin/coffee
測試您的變更,
bin/cake test
執行測試套件,
bin/cake build
重新建置完整的 CoffeeScript 編譯器,以及
bin/cake build:except-parser
如果您沒有編輯 grammar.coffee
,則可以重新編譯得更快。
git checkout lib && bin/cake build:full
是在您處理核心語言時可以執行的良好指令。它將更新 lib
資料夾(如果您弄壞了某些東西),建置您已變更的編譯器,使用它來重新建置它本身(一個良好的健全性測試),然後執行所有測試。如果它們通過,則表示您很有可能已成功變更。
瀏覽器測試
在您的目前瀏覽器中執行 CoffeeScript 的測試套件。
CoffeeScript 問題
錯誤報告、功能建議和語言變更的想法都屬於這裡。
CoffeeScript Google 群組
如果您想詢問問題,則寄件清單是獲得協助的好地方。
CoffeeScript Wiki
如果您曾經學過一個整齊的 CoffeeScript 技巧或訣竅,或遇到一個陷阱,請在 wiki 上分享。wiki 也提供了一個方便的 文字編輯器擴充功能、網頁架構外掛程式 和一般 CoffeeScript 建置工具 目錄。
常見問題
也許您的 CoffeeScript 相關問題以前已經被詢問過。請先查看常見問題。
JS2Coffee
是一個非常好的反向 JavaScript 轉 CoffeeScript 編譯器。它不會是完美的(推論您的 JavaScript 類別是什麼,當您需要繫結函式時,等等…)——但它是轉換簡單指令碼的絕佳起點。
高解析度標誌
CoffeeScript 標誌提供 SVG 格式,可供簡報使用。
通常可以在 CoffeeScript IRC 室中找到快速協助和建議。加入 irc.freenode.net
上的 #coffeescript
,或按一下下方的按鈕,開啟此頁面上的網路聊天對話。
return
和 throw
參數相關的回歸。_
。return
和 export
關鍵字現在可以接受內隱物件(由縮排定義,不需要大括號)。\u{1F4A9}
)。coffee
指令現在會先查看目前資料夾的 node_modules
中是否已安裝 CoffeeScript,如果已安裝,就會執行該資料夾中的 coffee
二進位檔;否則,就會執行全球安裝的二進位檔。這讓您可以全球安裝一個版本的 CoffeeScript,並針對特定專案在本地端安裝不同的版本。(cake
指令也是如此。)if
陳述式中 ?
運算子產生不正確程式碼的錯誤。import
和 export
陳述式中,default
、from
、as
和 *
的處理方式更佳。現在您可以匯入或匯出名為 default
的成員,而編譯器不會將其解釋為 default
關鍵字。cake
指令已更新,大部分工作任務都有新的 watch
選項。複製 CoffeeScript repo 並在 repo 的根目錄執行 cake
以查看選項。export
參照變數會阻止宣告變數的錯誤。coffee
指令對 .litcoffee
檔案不起作用的錯誤。@
值現在可以用作 for
表達式中的指標。這放寬了 for
表達式的編譯,允許指標變數為 @
值,例如 do @visit for @node, @index in nodes
。在 @visit
中,目前節點的指標 (@node
) 可用作 @index
。Error.prepareStackTrace
已還原,並進行了一些修改,應可防止錯誤的例外狀況,這些例外狀況讓一些下游專案難以執行。這修正了自 1.12.2 以來堆疊追蹤中不正確的行號。//=
算子的輸出現在會在右運算元周圍加上括號,就像其他指定算子一樣。MINIFY=false cake build:browser
再次建置為未壓縮版本。Error.prepareStackTrace
的錯誤修補版本。default
的模組成員,例如 import { default } from 'lib'
。不過就像在 ES2015 中一樣,您無法匯入整個模組並將其命名為 default
(因此不允許 import default from 'lib'
)。from
作為變數名稱會中斷 for
迴圈宣告的回歸。紀錄一下,from
在 CoffeeScript 中並非保留字;你可以將它用於變數名稱。from
在 import
和 export
陳述式,以及 for
迴圈的宣告中,會像關鍵字一樣運作;儘管如此,你也可以在這些情況下使用名為 from
的變數,而編譯器應該能夠分辨出差異。for…from
語法,用於輸出 ES2015 for…of
。(很抱歉它們無法匹配,但我們首先想出 for…of
來做別的事。)這允許迭代產生器或任何其他可迭代物件。請注意,在你的程式碼中使用 for…from
,表示你有責任確保你的執行時間支援 for…of
,或進一步將輸出的 JavaScript 轉譯到你的目標執行時間支援的版本。```
) 可讓您建立嵌入式 JavaScript 區塊,其中不需要跳脫單引號,這應可改善與 ES2015 範本字串和 Markdown 的互通性。\`
來跳脫反引號。import
和 export
中僅限 CoffeeScript 的關鍵字現在會被忽略。"""
字串中縮排移除的錯誤。2e308
(就像所有其他大型數字字面值一樣)。CoffeeScript 現在支援 ES2015 import
和 export
語法。
已將 -M, --inline-map
旗標新增至編譯器,讓您能將原始碼對應直接嵌入輸出 JavaScript 中,而不是作為一個獨立檔案。
針對 yield
的一堆修正
yield return
不再能被誤用為表達式。
yield
現在與 return
類似,它既可用作獨立運算式,也可與表達式一起使用。在您之前撰寫 yield undefined
的地方,現在可以簡單地撰寫 yield
。但是,這也意味著繼承了 return
所具有的相同的語法限制,因此這些範例不再編譯
doubles = -> yield for i in [1..3] i * 2 six = -> yield 2 * 3
JavaScript 輸出稍微好一點,不再有 yield
周圍不必要的括號和空格、雙重縮排和雙重分號。
&&=
、||=
、and=
和 or=
不再意外地允許等號前有空格。
改進了幾個錯誤訊息。
就像 undefined
編譯為 void 0
一樣,NaN
現在編譯為 0/0
,Infinity
編譯為 2e308
。
修復了具有預設值的重新命名解構參數的錯誤修正。({a: b = 1}) ->
不再會導致編譯器崩潰。
改進了 CoffeeScript 程式內部的表示。這只會影響使用 CoffeeScript.tokens
或 CoffeeScript.nodes
的工具。此類工具需要更新以考慮已變更或已新增的符號和節點。
幾個次要錯誤修正,包括
catch
區塊中捕獲的錯誤不再不必要地宣告,也不再錯誤地將沒有 catch
的 try
區塊命名為 undefined
。coffee -e 'throw null'
不再會崩潰。.exit
退出 REPL 時,REPL 不再會當機。for
迴圈時,不再會輸出無效的 JavaScript。CoffeeScript 現在支援 ES2015 風格的解構預設值。
(offsetHeight: height) ->
不再編譯。該語法是意外產生的,而且部分損壞。請改用 ({offsetHeight: height}) ->
。物件解構總是需要大括號。
幾個次要錯誤修正,包括
catch
區塊中已捕獲錯誤的變數名稱衝突。--bare
選項編譯時,行開頭的標記來源對應。這會產生較小的來源對應,這是個不錯的附加效果。on
、off
、yes
和 no
的錯誤訊息。this
的運算式周圍的 yield
錯誤。-r
選項,該選項允許在使用 --eval
或 --interactive
執行前,先載入模組。<script type="text/coffeescript">
標籤中,為避免瀏覽器重複要求 .coffee 檔案,您現在可以使用 data-src
屬性,取代 src
。yield
相關的邊緣案例,包括 yield return
和 yield throw
。yield
的函式。@example
函式參數不再可以在函式主體中作為赤裸的 example
變數使用。--join
選項現在已棄用。.js.map
作為檔案副檔名,而不是只有 .map
。%%
運算子現在只強制轉換其右運算元一次。require 'coffee-script/repl'
需要。coffee
二進位檔直接執行腳本時中斷節點模組查詢。require 'coffee-script/register'
或 CoffeeScript.register()
來完成。此外,對於 Mocha 等設定,請使用 coffee-script/register。//#
語法。.
現在會關閉所有開啟的呼叫,允許更簡潔的串接語法。$ 'body'
.click (e) ->
$ '.box'
.fadeIn 'fast'
.addClass '.active'
.css 'background', 'white'
$('body').click(function(e) {
return $('.box').fadeIn('fast').addClass('.active');
}).css('background', 'white');
**
、//
和 %%
運算子,以及參數清單和解構式中的 ...
展開。super
不允許在方法外,而且在 for
迴圈內正確運作。-p
資料夾。options
物件不再會被變異。require
來載入 .coffee.md
Literate CoffeeScript 檔案。在瀏覽器中,使用 text/literate-coffeescript
script 標籤。coffee --lint
指令已移除。在最初處理編譯器時,這項指令很有用,但已被 JSHint 取代。您現在可以使用 -l
來傳遞 Literate 檔案到 stdio。catch
,以及可執行類別主體與原型屬性附加的錯誤。coffee
指令執行 CoffeeScript 時提供自動列對應,以及在瀏覽器中直接執行 CoffeeScript 時提供自動列對應。此外,也為編譯器所拋出的語意錯誤提供更好的錯誤訊息 — 甚至還有色彩。coffee
指令現在可以正確地 fork()
.coffee
和 .js
檔案。(需要 Node.js 0.9+)--map
旗標傳遞給編譯器,然後就可以開始了。請將所有感謝傳達給 Jason Walton。.coffee.md
現在也支援為現有工具而設的 Literate CoffeeScript 檔案副檔名。.litcoffee
仍然是標準的。super
的幾個小錯誤。for item in list by -1
registerExtension
,並從 path.exists
移至 fs.exists
,修正 Node/編譯器棄用警告。super
(遞迴向上走)。eval
或 arguments
的值等等。請參閱 #1547 的完整討論。Ctrl-V
進入多行模式。您現在也可以將輸入直接導向到 REPL。由 CoffeeScript VERSION 產生
標頭。a or= b
現在視為語法錯誤。do
的語意,現在可以更輕鬆地模擬命名空間:do (x = 1, y = 2) -> …
coffee --watch
進行其他調整和改善。如果您在受監控的指令碼中引入語法錯誤,Watch 現在會預設發出嗶聲。現在我們在遞迴監控時,預設會忽略隱藏目錄。coffee --watch
和 --join
進行多項改善。現在您可以同時使用這兩個指令,以及在 --watch
的資料夾中新增和移除檔案和目錄。throw
陳述式作為表達式的一部分。this
。super
,而繫結類別方法現在會保留其正確的內容。010 為 8
和十六進位數字 0xf 為 15
,但 CoffeeScript 現在也支援二進位數字:0b10 為 2
。require
各個元件,而無需使用 npm。例如,將 CoffeeScript 資料夾新增至您的路徑後:require('coffee-script/lexer')
coffee --watch
功能現在僅適用於 Node.js 0.6.0 及更高版本,但現在也能在 Windows 上正常運作。修正區塊註解格式化、?=
編譯、控制結構的隱式呼叫、try/catch 區塊的隱式呼叫、變數參數從區域範圍外洩、heregexes 之後語法錯誤的行號、括號數字文字的屬性存取、繫結類別方法和具有保留名稱的 super、REPL 大修、連續編譯的分號、隱式呼叫物件中的區塊註解,以及 Chrome 錯誤。
修正具有外部建構函式的類別錯誤,請參閱問題 #1182。
透過 coffee
可執行檔執行時,process.argv
和相關函式現在會回報 coffee
,而不是 node
。與 Node.js 0.4.x 模組查詢變更有更好的相容性。REPL 中的輸出現在會上色,就像 Node 的輸出一樣。現在使用 --join
時,必須為串接的 CoffeeScripts 命名。修正意外將複合除法 /=
詞法分析為正規表示式的錯誤。所有 text/coffeescript
標籤現在都應該按照包含的順序執行。修正使用外部建構函式的延伸子類別問題。修正 addImplicitParentheses
中的邊緣案例無限迴圈。修正函式呼叫長鏈的指數遞減。全域變數不再外洩到 CoffeeScript REPL 中。splatted 參數宣告為函式的區域變數。
修復了 Unicode 識別碼的詞法分析錯誤。更新 REPL 以與 Node.js 0.3.7 相容。修復了 REPL 中需要相對路徑的問題。現在會最佳化掉尾隨的 return
和 return undefined
。不再需要核心 Node.js util
模組,以向後相容於 Node.js 0.2.5。修復了條件 return
會導致 switch
陳述式中發生穿透的問題。最佳化了解構賦值中的空物件。
當在迴圈主體中產生函式時,CoffeeScript 迴圈不再嘗試保留區塊範圍。相反地,你可以使用 do
關鍵字來建立一個方便的封閉包裝器。新增一個 --nodejs
旗標,用於將選項直接傳遞給 node
可執行檔。改善了在表達式中使用純粹陳述式的行為。修復了所有瀏覽器中透過 -1
的包含式切片,以及使用任意表達式作為端點的拼接。
REPL 現在正確地格式化堆疊追蹤,並在非同步例外中保持運作。現在使用 --watch
會在編譯檔案時印出時間戳記。修復了在拔取的封閉迴圈中意外洩漏的一些變數。建構函式現在會在類別主體中維護其宣告位置。已移除動態物件金鑰。現在支援巢狀類別。修復了裸散列函式的執行內容。修復了連鎖比較的反轉錯誤。連鎖類別實例化現在可以正確地與散列搭配使用。
0.9.5 應視為 CoffeeScript 1.0 的第一個候選版本。自前一個版本以來,已進行大量內部變更,其中許多是從 satyr 的 CoffeeScript 方言 Coco 中貢獻的。已新增 Heregexes(延伸正規表示式)。函式現在可以有預設引數。類別主體現在是可執行程式碼。改善 CoffeeScript 無效語法的語法錯誤。undefined
現在運作方式類似於 null
,且無法指定新的值。單行理解的優先順序已變更:result = i for i in list
以前預設會分析為 result = (i for i in list)
… 現在則會分析為 (result = i) for i in list
。
CoffeeScript 現在使用適當命名的暫時變數,並在使用後回收其參照。已新增 require.extensions
支援 Node.js 0.3。現在在瀏覽器中載入 CoffeeScript 只會將單一 CoffeeScript
物件新增至全域範圍。修正隱式物件和區塊註解的邊界狀況。
CoffeeScript switch
陳述式現在編譯成 JS switch
陳述式,它們先前編譯成 if/else
鏈以符合 JavaScript 1.3。現在支援浸泡函式呼叫。RubyMine 編輯器使用者現在應該可以使用 --watch
模式。
現在可以選擇指定範圍文字的開始和結束,例如 array[3..]
。現在您可以說 a not instanceof b
。修正了巢狀顯著和不顯著縮排的重要錯誤(問題 #637)。新增一個 --require
旗標,讓您可以連接到 coffee
指令。新增一個自訂 jsl.conf
檔案,供我們偏好的 JavaScriptLint 設定使用。透過將運算規則扁平化,加快了 Jison 語法編譯時間。現在可以使用 JavaScript-minifier 友善語法來使用區塊註解。新增 JavaScript 的複合賦值位元運算子。修正了隱式物件文字的錯誤,這些文字具有數字和字串開頭鍵,作為隱式呼叫的主題,以及作為複合賦值的一部分。
0.9.1 的錯誤修正版本。大幅改善了混合隱式物件、隱式函式呼叫和隱式縮排的處理。字串和正規表示式內插現在嚴格為 #{ … }
(Ruby 風格)。編譯器現在會採用 --require
旗標,它會指定在編譯前執行的指令碼。
The CoffeeScript 0.9 series is considered to be a release candidate for 1.0; let’s give her a shakedown cruise. 0.9.0 introduces a massive backwards-incompatible change: Assignment now uses =
, and object literals use :
, as in JavaScript. This allows us to have implicit object literals, and YAML-style object definitions. Half assignments are removed, in favor of +=
, or=
, and friends. Interpolation now uses a hash mark #
instead of the dollar sign $
— because dollar signs may be part of a valid JS identifier. Downwards range comprehensions are now safe again, and are optimized to straight for loops when created with integer endpoints. A fast, unguarded form of object comprehension was added: for all key, value of object
. Mentioning the super
keyword with no arguments now forwards all arguments passed to the function, as in Ruby. If you extend class B
from parent class A
, if A
has an extended
method defined, it will be called, passing in B
— this enables static inheritance, among other things. Cleaner output for functions bound with the fat arrow. @variables
can now be used in parameter lists, with the parameter being automatically set as a property on the object — useful in constructors and setter functions. Constructor functions can now take splats.
快速修正錯誤(緊接在 0.7.1 之後),解決在某些情況下無法解析 coffee
命令列選項的問題。
區塊式註解現在會傳遞並列印為 JavaScript 區塊註解,使其可應用於授權條款和版權標題。透過 hashbangs 改善執行獨立咖啡腳本的支援。改善語法中不存在的記號的語法錯誤。
CoffeeScript 變數的官方樣式現在為 camelCase,如同 JavaScript。保留字現在可作為物件金鑰,並會為您加上引號。範圍理解現在會產生更簡潔的程式碼,但若您想向下反覆運算,則必須指定 by -1
。語法錯誤的回報功能較前一版本大幅改善。現在以無引數執行 coffee
會啟動 REPL,並支援 Readline。<-
繫結運算子已從 CoffeeScript 中移除。新增 loop
關鍵字,等同於 while true
迴圈。包含封閉的理解現在會封閉其變數,如同 forEach
的語意。您現在可以在類別定義中使用繫結函式(繫結至執行個體)。為了一致性,a in b
現在是陣列存在檢查,而 a of b
是物件金鑰檢查。註解不再傳遞至產生的 JavaScript。
編譯滿是指令碼的目錄時,coffee
指令現在會保留目錄結構。修正兩個遺漏,這些遺漏會阻止 CoffeeScript 編譯器在 Internet Explorer 中執行。現在有一個區塊註解的語法,精神上類似於 CoffeeScript 的 here 文件。現在支援 ECMA Harmony DRY 風格的樣式比對,其中屬性的名稱與值的相同:{name, length}: func
。現在允許在理解變數中進行樣式比對。現在允許 unless
採用區塊形式。新增 until
迴圈,作為 while
迴圈的反向。現在允許 switch
陳述式沒有 switch 物件子句。相容於 Node.js v0.1.95。
升級 CoffeeScript 以與新的 Node.js v0.1.90 系列相容。
現在允許尾隨逗號,類似 Python。靜態屬性可以使用 @property
符號直接指定在類別定義中。
插值現在可以在正規表示式、heredoc 和字串中使用。增加了 <-
繫結運算子。允許指派給半表達式,而不是特殊的 ||=
類型的運算子。引數物件不再自動轉換成陣列。在需要 coffee-script
之後,Node.js 現在可以透過 registerExtension 直接載入 .coffee
檔案。現在可以在函式呼叫、陣列和模式配對中使用多個展開運算子。
字串插值,由 Stan Angeloff 貢獻。由於 --run
自 0.5.3 以來一直是預設值,因此將 --stdio
和 --eval
更新為預設執行,如果您想列印結果,請也傳遞 --compile
。
修正了 Node.js 全域常數 __filename
和 __dirname
的錯誤。調整以更靈活地剖析巢狀函數字面和縮排不當的註解。更新至最新的 Node.js API。
CoffeeScript 現在有定義類別的語法。許多核心組件(節點、詞法分析器、重寫器、範圍、Optparse)都在使用它們。Cakefile 可以使用 optparse.coffee
來定義任務的選項。--run
現在是 coffee
指令的預設旗標,使用 --compile
來儲存 JavaScript。修正了正規表示式字面和鏈式除法之間的歧義。
加入了壓縮版的編譯器,以 browser-compiler/coffee-script.js
的形式包含在網頁中。它會自動執行任何類型為 text/coffeescript
的腳本標籤。在 coffee
指令中加入了 --stdio
選項,用於管道編譯。
改進了使用存在運算子進行空值保護,包括索引屬性的保護。在 while
迴圈中加入了條件,因此您可以將它們與 when
一起用作篩選器,就像理解一樣。
CoffeeScript 0.5.0 是個重大版本,雖然語言本身沒有變動,但 Ruby 編譯器已被移除,取而代之的是用純 CoffeeScript 編寫的自託管編譯器。
@property
現在是 this.property
的簡寫。預設的 JavaScript 引擎已從 Narwhal 切換到 Node.js。如果您想繼續使用 Narwhal,請傳遞 --narwhal
旗標。
CoffeeScript 0.3 包含重大的語法變更:函數符號已變更為 ->
,而繫結函數符號現在是 =>
。函數定義中的參數清單現在必須用括號括起來。新增了屬性浸潤,使用 ?.
算子。在呼叫帶有參數的函數時,括號變為可選。移除了過時的區塊文字語法。
新增了 Python 風格的鏈式比較、條件存在算子 ?=
,以及一些來自《Beautiful Code》的範例。修正了與陳述式轉換為表達式、參數轉換為陣列,以及 TextMate 語法高亮顯示器相關的錯誤。
switch 陳述式中的條件現在可以一次取用多個值 — 如果其中任何一個為真,則會執行案例。新增長箭號 ==>
,用於定義並立即將函式繫結到 this
。while 迴圈現在可用作表達式,就像理解一樣。可以在模式比對中使用展開運算子來吸收陣列的其餘部分。
新增 ECMAScript Harmony 風格的解構賦值,用於處理從巢狀陣列和物件中擷取值。新增對縮排敏感的 here 文件,用於格式化良好的字串或程式碼區塊。
取消不令人滿意的 ino
關鍵字,並以 of
取代,用於物件理解。它們現在看起來像:for prop, value of object
。
在對物件執行理解時,請使用 ino
,而不是 in
,這有助於我們在編譯時產生更小、更有效的程式碼。新增 ::
作為表示 .prototype.
的簡寫。將「展開」符號從前置星號 *
變更為後置省略號 ...
。新增 JavaScript 的 in
運算子、空的 return
陳述式和空的 while
迴圈。現在,以大寫字母開頭的建構函式會包含安全檢查,以確保傳回物件的新執行個體。extends
關鍵字現在的功能與 Google Closure Library 中的 goog.inherits
相同。
參數物件現在在被參照時會轉換成真正的陣列。
重大版本。顯著的空白。更好的陳述式轉換為表達式。Splat。Splice 文字。物件理解。區塊。存在運算子。非常感謝所有張貼問題的人,特別感謝 Liam O’Connor-Davis 的空白和表達式協助。
修正從 CoffeeScript 目錄之外執行 coffee --interactive
和 --run
的錯誤。修正巢狀函式/if 陳述式的錯誤。
陣列切片文字和陣列理解現在都可以使用 Ruby 風格的範圍來指定開始和結束。JavaScript 變數宣告現在會推送到範圍的頂端,讓所有指定陳述式都變成表達式。您可以使用 \
來跳脫換行。coffee-script
指令現在稱為 coffee
。
官方 CoffeeScript 擴充套件現在是 .coffee
,而不是 .cs
,後者應屬於 C#。由於廣受歡迎,您現在也可以使用 =
來指派。與 JavaScript 不同,=
也可以在物件文字中使用,與 :
可以互換。針對 func(1)(2)(3)(4)
等連鎖函式呼叫進行了語法修正。繼承和 super 不再使用 __proto__
,因此現在應該與 IE 相容。
coffee
指令現在包含 --interactive
,它會啟動一個互動式 CoffeeScript 會話,以及 --run
,它會直接編譯和執行一個腳本。這兩個選項都依賴於 Narwhal 的工作安裝。aint
關鍵字已被 isnt
取代,它與 is
搭配起來更順暢。引號字串現在可以在物件文字中作為識別碼:例如 {"5+5": 10}
。所有指派運算子現在都使用冒號:+:
、-:
、*:
等。
修正透過多層繼承呼叫 super()
的錯誤,重新加入 extends
關鍵字。加入實驗性的 Narwhal 支援(作為 Tusk 套件),由 Tom Robinson 貢獻,包含 bin/cs 作為 CoffeeScript REPL 和直譯器。新的 --no-wrap
選項用於抑制安全函式包裝器。
加入 instanceof
和 typeof
作為運算子。
初始 CoffeeScript 發行。