使用CoffeeScrip優(yōu)美方式編寫(xiě)javascript代碼
JavaScript無(wú)疑是在web最偉大的發(fā)明之一,幾乎一切網(wǎng)頁(yè)動(dòng)態(tài)效果都是基于它豐富的計(jì)算能力。而且它的能力在各種新的JavaScript的Engine下也越來(lái)越強(qiáng)了,比如Google Chrome用的V8 Engine。
但是由于誕生的太早,有很多語(yǔ)法定義在今天看來(lái)有些效率低下了,一些更加先進(jìn)的語(yǔ)法形式,由于歷史原因,沒(méi)辦法加入到現(xiàn)在的JavaScript語(yǔ)言中,可以說(shuō)一種遺憾。
世界上的很多天才都在為構(gòu)建更好的JavaScript而努力。已經(jīng)有了很多嘗試,其中最有前途的,無(wú)非就是CoffeeScript和TypeScript了。面對(duì)CoffeeScript,我有一見(jiàn)如故的感覺(jué);而TypeScript也激發(fā)了我極大的興趣。CoffeeScript和TypeScript一樣,都是編譯為JavaScript的語(yǔ)言,它們都增強(qiáng)了JavaScript的表達(dá)能力。這篇文章是講CoffeeScript的,TypeScript將放在下一篇再講。
所謂編譯為JavaScript,是指CoffeeScript和TypeScript沒(méi)有實(shí)現(xiàn)自己的運(yùn)行時(shí),它們都是編譯為等價(jià)的JavaScript代碼,然后放在JavaScript的解釋器上運(yùn)行。
CoffeeScript
簡(jiǎn)潔性
CoffeeScript給人最大的印象就是其簡(jiǎn)潔的表達(dá)。下面代碼是我從CoffeeScript中文摘抄下來(lái)的:
# 賦值: number = 42 opposite = true # 條件: number = -42 if opposite # 函數(shù): square = (x) -> x * x # 數(shù)組: list = [1, 2, 3, 4, 5] # 對(duì)象: math = root: Math.sqrt square: square cube: (x) -> x * square x # Splats: race = (winner, runners...) -> print winner, runners # 存在性: alert "I knew it!" if elvis? # 數(shù)組 推導(dǎo)(comprehensions): cubes = (math.cube num for num in list)
上面的代碼會(huì)編譯為等價(jià)的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力求簡(jiǎn)潔。其簡(jiǎn)潔性首先表現(xiàn)在對(duì)一些僅用于語(yǔ)法控制的符號(hào)進(jìn)行了去除。這其中包括:
取消分號(hào)
取消var聲明
取消大括號(hào)包圍內(nèi)層代碼,使用縮進(jìn)取代
函數(shù)調(diào)用在沒(méi)有歧義的情況下可以省略括號(hào)
var聲明涉及到復(fù)雜又很雞肋的JavaScript變量作用域機(jī)制。這部分內(nèi)容先放著不講。CoffeeScript通過(guò)完全取消var聲明機(jī)制而使得問(wèn)題得到簡(jiǎn)化??傊贑offeeScript世界里,變量不用事先聲明,直接用就是了。而且這種用法基本沒(méi)有什么危險(xiǎn)。
縮進(jìn)在CoffeeScript中不僅僅在于美化代碼,其代表了代碼層次的組織,是有特別的含義的。簡(jiǎn)單地說(shuō)就是,不適用大括號(hào)包圍內(nèi)層代碼,而是內(nèi)層代碼要縮進(jìn)。不同的縮進(jìn)代表了不同的代碼層次。形式和內(nèi)容是一致的。
縮進(jìn)的例子:
#if縮進(jìn) if true 'true' else 'false' #while縮進(jìn) while true 'true' #函數(shù)縮進(jìn) (n) -> n * n #對(duì)象字面量縮進(jìn) kids = brother: name: "Max" age: 11 sister: name: "Ida" age: 9
在不引起歧義的情況下,CoffeeScript的函數(shù)調(diào)用可以省略括號(hào)。例如console.log(object)可以簡(jiǎn)化為console.log object。所謂引起歧義的一個(gè)例子就是無(wú)參數(shù)的情況下,console.log就不知道是取出函數(shù)型屬性log還是調(diào)用函數(shù)log了。
CoffeeScript的函數(shù)表達(dá)式也做了極致的精簡(jiǎn)精簡(jiǎn)。一個(gè)單行函數(shù)的定義可以這樣:
square = (x) -> x * x
而多行函數(shù)也是通過(guò)縮進(jìn)來(lái)組織的。一個(gè)空的函數(shù)最為簡(jiǎn)潔,是這樣:->。
函數(shù)的這種簡(jiǎn)潔表達(dá)使得傳遞回調(diào)函數(shù)非常便利。一個(gè)數(shù)組的map可能像這樣就足夠了:
list = [1, 2, 3] list.map (e) -> e+1
而等效的JavaScript代碼就不能這么馬虎:
list = [1, 2, 3]; list.map(function(e) { return e + 1; });
增強(qiáng)的表達(dá)
CoffeeScript提供了JavaScript所沒(méi)有的一些強(qiáng)大的表達(dá)語(yǔ)法,這也是被稱為語(yǔ)法糖的東西。在我印象中,這種增強(qiáng)性是很多的,我舉出兩個(gè)有代表性的例子:
字符串插值法
列表解析
其中字符串插值法是對(duì)現(xiàn)有字符串能力的一種擴(kuò)充和語(yǔ)法上的簡(jiǎn)化;而列表解析就要涉及到觀念上的改變了。前者是一種改良,后者則是一種變革。
字符串插值法
在CoffeeScript的字符串里,可以用#{…}嵌入一個(gè)表達(dá)式。例如:
"#{ 22 / 7 } is a decent approximation of π"
等價(jià)于:
"" + (22 / 7) + " is a decent approximation of π";
插值在這里起到占位的作用,使得動(dòng)態(tài)內(nèi)容的字符串更容易構(gòu)建。我想人人都能接受這樣的表達(dá)。
列表解析
列表解析是CoffeeScript的世界里的重要一員。它改變了循環(huán)的思路。CoffeeScript沒(méi)有提供像JavaScript那樣的for循環(huán)結(jié)構(gòu),而是統(tǒng)統(tǒng)轉(zhuǎn)化為列表解析。一個(gè)常規(guī)的JavaScript for循環(huán),像下面這樣:
food_list = ['toast', 'cheese', 'wine']; for (i = 0, len = food_list.length; i < len; i++) { food = food_list[i]; eat(food); }
用CoffeeScript實(shí)現(xiàn)就是:
food_list = ['toast', 'cheese', 'wine']
eat food for food in food_list #做個(gè)小補(bǔ)充,for循環(huán)的單條語(yǔ)句的寫(xiě)法
單單是上面的例子不足以顯示列表解析的強(qiáng)大(卻看到它的簡(jiǎn)潔了)。在繼續(xù)這個(gè)話題之前,我覺(jué)得我有必要補(bǔ)充一下另一個(gè)涉及到CoffeeScript理念的東西了:一切皆是表達(dá)式。
在CoffeeScript世界里,一切語(yǔ)句都是表達(dá)式語(yǔ)句,都會(huì)返回一個(gè)值。函數(shù)調(diào)用默認(rèn)會(huì)返回最后一條語(yǔ)句的值。if條件結(jié)構(gòu)也會(huì)返回值,其返回的是執(zhí)行的最后一條語(yǔ)句的值。循環(huán)結(jié)構(gòu)有些不同,其會(huì)將每次循環(huán)的結(jié)果都保存在一個(gè)數(shù)組里,作為此循環(huán)結(jié)構(gòu)的值。例如下面代碼的list結(jié)果就是[5, 4, 3, 2, 1]。
num = 6 list = while num -= 1 num
回到列表解析的主題。與while一樣,for結(jié)構(gòu)也是一種循環(huán)的表達(dá),其結(jié)果也是一個(gè)數(shù)組?;氐较惹暗睦樱旅娴男〈a的list結(jié)果就是['t', 'c', 'w']。
food_list = ['toast', 'cheese', 'wine'] list = (food[0] for food in food_list)
我們已經(jīng)看到for循環(huán)的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')
列表解析的特色的地方在于它改變了我們組織循環(huán)的方式和解析數(shù)組的模式。這是一種聲明式的編程方法,告訴程序你想要什么而不去關(guān)心構(gòu)建的過(guò)程。
類的支持
類是CoffeeScript對(duì)JavaScript的一個(gè)很重要的補(bǔ)充。JavaScript的原型功能很強(qiáng)大,寫(xiě)法上又恨別扭。正確地設(shè)置原型鏈以實(shí)現(xiàn)繼承關(guān)系也是個(gè)很大的挑戰(zhàn)。CoffeeScript從語(yǔ)法上直接支持類的定義,自然且隱藏細(xì)節(jié)。
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()
從實(shí)現(xiàn)上來(lái)說(shuō),CoffeeScript的類與JavaScript的構(gòu)造函數(shù)和原型鏈那一套并無(wú)二致。所以,理解原型機(jī)制也是理解CoffeeScript類的基礎(chǔ)。
關(guān)于JavaScript的糟粕
CoffeeScript的另一個(gè)目標(biāo)是從語(yǔ)法層面上直接消除JavaScript的被人詬病的一些糟粕部分。前面已經(jīng)說(shuō)過(guò)關(guān)于分號(hào)的部分。關(guān)于var聲明的部分。分號(hào)的機(jī)制暫且不去例會(huì),總之CoffeeScript不用再去寫(xiě)分號(hào)了。
在JavaScript當(dāng)中,最為人詬病的糟粕部分有兩處,因?yàn)樗鼈兪褂玫那闆r最多而且容易出錯(cuò)。
全局變量
相等比較
全局變量
JavaScript的作用域規(guī)則很復(fù)雜,涉及到var聲明機(jī)制和變量提升。在JavaScript里,構(gòu)造一個(gè)全局變量是很容易的,有三種方式:
在全局的環(huán)境里用var聲明
var name = 'name';
在函數(shù)內(nèi)用省略var的方式定義
function foo() { name = 'name'; }
綁定到window的屬性
window.name = 'name';
其中第1種和第2種方式是最常見(jiàn)的錯(cuò)誤用法。首先不推薦直接在全局環(huán)境中編碼,而是應(yīng)該用一個(gè)匿名函數(shù)包裹起來(lái),將程序的作用域限制在這個(gè)匿名函數(shù)中。第二種用法完完全全就是忘記了var聲明。而我在實(shí)際的JavaScript編碼中,忘記var聲明是常有的事(就像經(jīng)常忘記行末補(bǔ)上分號(hào)一樣)。
而在CoffeeScript里面就完全沒(méi)有這種擔(dān)心了。首先,編譯后的JavaScript代碼不會(huì)暴露在全局環(huán)境里,所有的代碼都是自動(dòng)包裹在一個(gè)匿名函數(shù)(function(){ ... })();內(nèi)。然后,所有的變量都會(huì)自動(dòng)加上var聲明。這就使得不小心污染全局的情況很難發(fā)生,除非使用賦值到window上。
相等比較
我們都知道JavaScript有兩種比較運(yùn)算符:==和===。我們也知道==在使用的過(guò)程中會(huì)很坑,所以平時(shí)都寧愿多打一個(gè)字符而使用===。CoffeeScript的只有一種比較運(yùn)算符==,而它會(huì)編譯成JavaScript的===,從而很好地避過(guò)了這道坑。
是否該使用CoffeeScript
CoffeeScript簡(jiǎn)化和增強(qiáng)了JavaScript的表達(dá)能力,盡可能地從語(yǔ)法層面上就能避免JavaScript的一些坑。用它寫(xiě)代碼,會(huì)讓人有更清晰舒適的感覺(jué),而且不容易犯錯(cuò)。CoffeeScript的初衷就是提供更好的JavaScript。
然而,CoffeeScript與JavaScript是不兼容的。它既不是JavaScript的子集,也不是超集,而是與JavaScript有著顯然不同思路的一種語(yǔ)言。用CoffeeScript編程就必然要轉(zhuǎn)換觀念,盡管這種觀念更好更自然,但卻是有些固步自封的人望而卻步的主要原因了。
CoffeeScript并不是適合每一個(gè)人的。有些人對(duì)于用縮進(jìn)組織代碼層次完全不能接受,也不能接受用箭頭函數(shù)表達(dá)法。對(duì)于他們來(lái)說(shuō),去掉function關(guān)鍵字和大括號(hào)的組織怎么看都怎么地不順眼。
列表解析很強(qiáng)大,卻也顯得過(guò)于簡(jiǎn)潔了。對(duì)于習(xí)慣了構(gòu)造冗雜JavaScript程序的人們來(lái)說(shuō),并不習(xí)慣這種表達(dá)方式。
總之,是不可強(qiáng)求別人去學(xué)習(xí)使用CoffeeScript。JavaScript已經(jīng)足夠強(qiáng)大,只要足夠小心,完全可以使用JavaScript很好地完成工作。對(duì)于那些想要嘗試CoffeeScript,我們也要給予鼓勵(lì)的態(tài)度,他們是求新求變的勇士。CoffeeScript真的值得一試,而且它真的很小巧,完全掌握它不是件困難的事。
對(duì)于在團(tuán)隊(duì)推行CoffeeScript,我本人更是持有保守的看法。如果團(tuán)隊(duì)從一開(kāi)始就使用CoffeeScript還好。如果是要從CoffeeScript轉(zhuǎn)為JavaScript,就要謹(jǐn)慎行之。一種可行的方式是先嘗試在一個(gè)小項(xiàng)目中使用CoffeeScrip,看看效果如何。
對(duì)于個(gè)人來(lái)說(shuō),就沒(méi)有什么限制了。如果真的喜歡,就去嘗試吧。你可以使用CoffeeScript寫(xiě)腳本,構(gòu)建自己的網(wǎng)站,做一些小玩意。
以上內(nèi)容是小編給大家介紹的使用CoffeeScrip優(yōu)美方式編寫(xiě)javascript代碼,希望大家喜歡。
相關(guān)文章
原生js實(shí)現(xiàn)移動(dòng)端瀑布流式代碼示例
這篇文章主要為大家分享了原生js實(shí)現(xiàn)移動(dòng)端瀑布流式代碼示例,對(duì)瀑布流布局感興趣的小伙伴們可以參考一下2015-12-12詳解微信小程序框架wepy踩坑記錄(與vue對(duì)比)
這篇文章主要介紹了詳解微信小程序框架wepy踩坑記錄(與vue對(duì)比),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03p5.js 畢達(dá)哥拉斯樹(shù)的實(shí)現(xiàn)代碼
這篇文章主要介紹了p5.js 畢達(dá)哥拉斯樹(shù)的實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03javaScript如何處理從java后臺(tái)返回的list
把List集合轉(zhuǎn)成json格式傳到界面上,這樣js里面操作也簡(jiǎn)單,具體的解決方法如下,大家可以參考學(xué)習(xí)下2014-04-04js獲取checkbox復(fù)選框選中的選項(xiàng)實(shí)例
這篇文章主要介紹了js如何獲取checkbox復(fù)選框選中的選項(xiàng),比較適合新手,需要的朋友可以參考下2014-08-08JavaScript獲取移動(dòng)設(shè)備型號(hào)的實(shí)現(xiàn)代碼(JS獲取手機(jī)型號(hào)和系統(tǒng))
這篇文章主要介紹了JavaScript獲取移動(dòng)設(shè)備型號(hào)的實(shí)現(xiàn)代碼,需要的朋友可以參考下2018-03-03