JavaScript無疑是web最偉大的發明之一,幾乎一切網頁動態效果都是基於它豐富的運算能力。而且它的能力在各種新的JavaScript的Engine下也越來越強了,像是Google Chrome用的V8 Engine。
但是由於誕生的太早,有很多語法定義在今天看來有些效率低下了,一些更加先進的語法形式,由於歷史原因,沒辦法加入到現在的JavaScript語言中,可以說一種遺憾。
世界上的許多天才都在為建立更好的JavaScript而努力。已經有了許多嘗試,其中最有前景的,無非就是CoffeeScript和TypeScript了。面對CoffeeScript,我有一見如故的感覺;而TypeScript也激發了我極大的興趣。 CoffeeScript和TypeScript一樣,都是編譯為JavaScript的語言,它們都增強了JavaScript的表達能力。這篇文章是講CoffeeScript的,TypeScript將放在下一篇再講。
所謂編譯為JavaScript,是指CoffeeScript和TypeScript沒有實作自己的執行時,它們都是編譯為等價的JavaScript程式碼,然後放在JavaScript的解釋器上執行。
CoffeeScript
簡潔性
CoffeeScript給人最大的印象就是其簡潔的表達。下面程式碼是我從CoffeeScript中文摘抄下來的:
# 赋值: number = 42 opposite = true # 条件: number = -42 if opposite # 函数: square = (x) -> x * x # 数组: list = [1, 2, 3, 4, 5] # 对象: math = root: Math.sqrt square: square cube: (x) -> x * square x # Splats: race = (winner, runners...) -> print winner, runners # 存在性: alert "I knew it!" if elvis? # 数组 推导(comprehensions): cubes = (math.cube num for num in list)
上面的程式碼會編譯為等價的JavaScript程式碼:
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; })(); run: cubes
CoffeeScript力求簡潔。其簡潔性首先表現在對一些僅用於語法控制的符號進行了去除。這其中包括:
取消分號
取消var聲明
取消大括號包圍內層程式碼,使用縮排取代
函數呼叫在沒有歧義的情況下可以省略括號
var聲明涉及複雜又很雞肋的JavaScript變數作用域機制。這部分內容先放著不講。 CoffeeScript透過完全取消var聲明機製而使得問題得到簡化。總之,在CoffeeScript世界裡,變數不用事先聲明,直接用就是了。而且這種用法基本上沒有什麼危險。
縮排在CoffeeScript中不僅僅在於美化程式碼,其代表了程式碼層次的組織,是有特別的意義的。簡單來說就是,不適用大括號包圍內層程式碼,而是內層程式碼要縮排。不同的縮排代表了不同的程式碼層次。形式和內容是一致的。
縮排的範例:
#if缩进 if true 'true' else 'false' #while缩进 while true 'true' #函数缩进 (n) -> n * n #对象字面量缩进 kids = brother: name: "Max" age: 11 sister: name: "Ida" age: 9
在不造成歧義的情況下,CoffeeScript的函數呼叫可以省略括號。例如console.log(object)可以簡化為console.log object。所謂引起歧義的一個例子就是無參數的情況下,console.log就不知道是取出函數型屬性log還是呼叫函數log了。
CoffeeScript的函數表達式也做了極致的精簡精簡。一個單行函數的定義可以這樣:
square = (x) -> x * x
而多行函數也是透過縮進來組織的。一個空的函數最簡潔,就是這樣:->。
函數的這種簡潔表達使得傳遞回調函數非常便利。一個陣列的map可能像這樣就夠了:
list = [1, 2, 3] list.map (e) -> e+1
而等效的JavaScript程式碼就不能這麼馬虎:
list = [1, 2, 3]; list.map(function(e) { return e + 1; });
增強的表達
CoffeeScript提供了JavaScript所沒有的一些強大的表達語法,這也是被稱為語法糖的東西。在我印像中,這種增強性是很多的,我舉出兩個有代表性的例子:
字串內插法
列表解析
其中字串插值法是對現有字串能力的一種擴充和語法上的簡化;而列表解析就要涉及到觀念上的改變了。前者是一種改良,後者則是一種變革。
字串內插法
在CoffeeScript的字串裡,可以用#{…}嵌入一個表達式。例如:
"#{ 22 / 7 } is a decent approximation of π"
等價於:
"" (22 / 7) " is a decent approximation of π";
插值在這裡起到佔位的作用,使得動態內容的字串更容易建構。我想人人都能接受這樣的表達。
列表解析
列表解析是CoffeeScript的世界裡的重要一員。它改變了循環的思路。 CoffeeScript沒有提供像JavaScript那樣的for迴圈結構,而是統統轉換為列表解析。一個常規的JavaScript for循環,像下面這樣:
food_list = ['toast', 'cheese', 'wine']; for (i = 0, len = food_list.length; i < len; i++) { food = food_list[i]; eat(food); }
用CoffeeScript实现就是:
food_list = ['toast', 'cheese', 'wine']
eat food for food in food_list #做个小补充,for循环的单条语句的写法
单单是上面的例子不足以显示列表解析的强大(却看到它的简洁了)。在继续这个话题之前,我觉得我有必要补充一下另一个涉及到CoffeeScript理念的东西了:一切皆是表达式。
在CoffeeScript世界里,一切语句都是表达式语句,都会返回一个值。函数调用默认会返回最后一条语句的值。if条件结构也会返回值,其返回的是执行的最后一条语句的值。循环结构有些不同,其会将每次循环的结果都保存在一个数组里,作为此循环结构的值。例如下面代码的list结果就是[5, 4, 3, 2, 1]。
num = 6 list = while num -= 1 num
回到列表解析的主题。与while一样,for结构也是一种循环的表达,其结果也是一个数组。回到先前的例子,下面的小代码的list结果就是['t', 'c', 'w']。
food_list = ['toast', 'cheese', 'wine'] list = (food[0] for food in food_list)
我们已经看到for循环的each形式
eat food for food in food_list
以及它的map形式
(food[0] for food in food_list)
下面给出它的filter形式
(food for food in food_list when food is 'wine')
列表解析的特色的地方在于它改变了我们组织循环的方式和解析数组的模式。这是一种声明式的编程方法,告诉程序你想要什么而不去关心构建的过程。
类的支持
类是CoffeeScript对JavaScript的一个很重要的补充。JavaScript的原型功能很强大,写法上又恨别扭。正确地设置原型链以实现继承关系也是个很大的挑战。CoffeeScript从语法上直接支持类的定义,自然且隐藏细节。
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()
从实现上来说,CoffeeScript的类与JavaScript的构造函数和原型链那一套并无二致。所以,理解原型机制也是理解CoffeeScript类的基础。
关于JavaScript的糟粕
CoffeeScript的另一个目标是从语法层面上直接消除JavaScript的被人诟病的一些糟粕部分。前面已经说过关于分号的部分。关于var声明的部分。分号的机制暂且不去例会,总之CoffeeScript不用再去写分号了。
在JavaScript当中,最为人诟病的糟粕部分有两处,因为它们使用的情况最多而且容易出错。
全局变量
相等比较
全局变量
JavaScript的作用域规则很复杂,涉及到var声明机制和变量提升。在JavaScript里,构造一个全局变量是很容易的,有三种方式:
在全局的环境里用var声明
var name = 'name';
在函数内用省略var的方式定义
function foo() { name = 'name'; }
Attributes bound to window
window.name = 'name';
The first and second methods are the most common incorrect usages. First of all, it is not recommended to code directly in the global environment. Instead, it should be wrapped in an anonymous function to limit the scope of the program to this anonymous function. The second usage is to completely forget the var declaration. In actual JavaScript coding, it is common for me to forget the var declaration (just like I often forget to add a semicolon at the end of the line).
In CoffeeScript, there is no such worry at all. First of all, the compiled JavaScript code will not be exposed to the global environment. All code is automatically wrapped in an anonymous function (function(){ ... })();. Then, all variables will be automatically declared with var. This makes it difficult to accidentally pollute the global situation, unless you use assignment to window.
Equality comparison
We all know that JavaScript has two comparison operators: == and ===. We also know that == can be tricky to use, so we usually prefer to type one more character and use ===. CoffeeScript has only one comparison operator ==, and it will be compiled into JavaScript's ===, thus avoiding this pitfall.
Should I use CoffeeScript
CoffeeScript simplifies and enhances the expression ability of JavaScript, and avoids some pitfalls of JavaScript as much as possible from the grammatical level. Using it to write code will make people feel clearer and more comfortable, and it will be less easy to make mistakes. The original intention of CoffeeScript is to provide better JavaScript.
However, CoffeeScript is incompatible with JavaScript. It is neither a subset nor a superset of JavaScript, but a language with obviously different ideas from JavaScript. Programming with CoffeeScript requires a change of concept. Although this concept is better and more natural, it is the main reason why some people who are stuck in their ways are deterred.
CoffeeScript is not suitable for everyone. Some people simply cannot accept the use of indentation to organize code hierarchies, nor the use of arrow function expressions. For them, the organization without the function keyword and braces is not pleasing to the eye.
List parsing is very powerful, but it is also too concise. For people who are used to constructing complex JavaScript programs, they are not used to this expression.
In short, you cannot force others to learn to use CoffeeScript. JavaScript is powerful enough that as long as you are careful enough, you can use JavaScript to get the job done well. For those who want to try CoffeeScript, we must also give an encouraging attitude. They are warriors seeking innovation and change. CoffeeScript is really worth a try, and it's really small and it's not difficult to master it completely.
I personally have a conservative view on implementing CoffeeScript in the team. It would be better if the team used CoffeeScript from the beginning. If you are switching from CoffeeScript to JavaScript, be careful. One possible way is to try using CoffeeScrip in a small project first to see how it works.
For individuals, there are no restrictions. If you really like it, go for it. You can use CoffeeScript to write scripts, build your own websites, and make some gadgets.
The above content is the beautiful way to write javascript code using CoffeeScrip introduced by the editor. I hope you like it.