A CoffeeScript programozási nyelv

Kifejezések

Minden nyelvnek megvannak a saját jól bevált kifejezés mintái és gyakorlatai. A CoffeeScripttel sincs másképp. Ez a fejezet feltárja ezeket a konvenciókat és megmutat néhány JavaScript és CoffeeScript-beli eltérést, így kapva gyakorlati képet a nyelvről.

Each

JavaScriptben a tömb minden elemén való végigiterálásához használhatjuk az újonnan hozzáadott forEach() funkciót vagy a régi C stílusú for ciklus. Ha tervezzük a JavaScript ECMAScript5-ben bevezetett legújabb funkcióinak a használatát, azt ajánlom, hogy használjunk kiegészítő könyvtárakat a régebbi böngészők támogatásához, amelyek emulálják ezeket a funkciókat.

// JavaScript for (var i=0; i < array.length; i++) myFunction(array[i]); array.forEach(function(item, i){ myFunction(item) });

Bár a forEach() szintaxisa sokkal tömörebb és olvashatóbb, attól a hátránytól szenved, hogy a callback funkció meghívódik minden egyes iterációnál a tömbön, ezért sokkal lassabb az ugyanolyan for ciklusnál. Lássuk ez hogyan néz ki CoffeeScriptben.

# CoffeeScript myFunction(item) for item in array // JavaScript var item, _i, _len; for (_i = 0, _len = array.length; _i < _len; _i++) { item = array[_i]; myFunction(item); }

Belátható, hogy olvashatóbb és tömörebb a szintaxis . Ami a legjobb, hogy for ciklussá fordul át a színfalak mögött. Más szavakkal a CoffeeScript szintaxisa ugyanolyan kifejezést kínál mint a forEach(), de a sebesség és a kiegészítő könyvtárak kifogások nélkül.

Map

A forEach()-hez hasonlóan, az ES5 bevezeti a natív map funkciót is, amely sokkal tömörebb mint a klasszikus for ciklus, név szerint a map()-et. Sajnálatosan, ez a kifejezés ugyanolyan hátrányoktól szenved mint a forEach(), a sebessége nagyban csökken a funkcióhívások által.

// JavaScript var result = [] for (var i=0; i < array.length; i++) result.push(array[i].name) var result = array.map(function(item, i){ return item.name; });

Ahogyan felfedtük a szintaxis fejezetben, a CoffeeScript listakezelés és listafeldolgozása használható a map()-hoz hasonló viselkedéshez. Figyeljük meg, hogy a listafeldolgozást zárójelek veszik körbe, ami kritikus abból a szempontból hogy biztosítsuk a feldolgozás azzal tér vissza amit várunk, a leképzett tömbbel.

# CoffeeScript result = (item.name for item in array) // JavaScript var item, result; result = (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = array.length; _i < _len; _i++) { item = array[_i]; _results.push(item.name); } return _results; })();

Select

Ugyancsak, a ES5 tartalmaz egy filter() funkciót a tömbök redukálására, szűrésére:

// JavaScript var result = [] for (var i=0; i < array.length; i++) if (array[i].name == "test") result.push(array[i]) result = array.filter(function(item, i){ return item.name == "test" });

A CoffeeScript szintaxisa a when kulcsszót használja az elemek szűréséhez egy összehasonlítással, kiválsztással. A háttérben egy for ciklus generálódik. Az egész végrehajtás egy anonymous funkcióban történik, hogy a funkció hatályokat betartsuk és a változó konfliktusokat elkerüljük.

# CoffeeScript result = (item for item in array when item.name is "test") // JavaScript var item, result; result = (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = array.length; _i < _len; _i++) { item = array[_i]; if (item.name === "test") { _results.push(item); } } return _results; })();

Ne felejtsük el a zárójeleket kirakni, máskülönben a `result` lesz az utolsó elem a tömbben. A CoffeeScript listafeldolgozás annyira rugalmas, hogy olyan hatékony kifejezéseket és kiválasztásokat is megenged mint amit következő példában láthatunk:

# CoffeeScript passed = [] failed = [] (if score > 60 then passed else failed).push score for score in [49, 58, 76, 82, 88, 90] # Or passed = (score for score in scores when score > 60) // JavaScript var failed, passed, score, _i, _len, _ref; passed = []; failed = []; _ref = [49, 58, 76, 82, 88, 90]; for (_i = 0, _len = _ref.length; _i < _len; _i++) { score = _ref[_i]; (score > 60 ? passed : failed).push(score); } passed = (function() { var _j, _len2, _results; _results = []; for (_j = 0, _len2 = scores.length; _j < _len2; _j++) { score = scores[_j]; if (score > 60) { _results.push(score); } } return _results; })();

Ha a listakifejezés túl hosszúra sikerülne, több sorba is törhetjük:

# CoffeeScript passed = [] failed = [] for score in [49, 58, 76, 82, 88, 90] (if score > 60 then passed else failed).push score // JavaScript var failed, passed, score, _i, _len, _ref; passed = []; failed = []; _ref = [49, 58, 76, 82, 88, 90]; for (_i = 0, _len = _ref.length; _i < _len; _i++) { score = _ref[_i]; (score > 60 ? passed : failed).push(score); }

Tartalmazások

A tömbben egy érték tartalmazásának leellenőrzését tipikusan az indexOf()-al oldjuk meg, ami elégg agyzsibbasztóan még mindig kiegészítő könyvtárat igényel, mert az Internet Explorer ezt a funkciót nem implementálta.

// JavaScript var included = (array.indexOf("test") != -1)

A CoffeeScript rendelkezik egy ügyes alternatívával, amelyet a Pythonisták már kiszúrhattak, név szerint az `in`-nel.

# CoffeeScript included = "test" in array // JavaScript var included; var __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (this[i] === item) return i; } return -1; }; included = __indexOf.call(array, "test") >= 0;

A háttérben, a CoffeeScript az Array.prototype.indexOf()-ot használja. Behúzza a kiegészítő könyvtárat, ha szükséges annak az észlelése, hogy az érték a tömbben van-e. Sajnálatosan ez azt jelenti hogy az `in` szintaxis nem működik stringekre. Vissza kell állnunk indexOf()-ra és ellenőriznünk, hogy a visszatérési érték negatív-e:

# CoffeeScript included = "a long test string".indexOf("test") isnt -1 // JavaScript var included; included = "a long test string".indexOf("test") !== -1;

Vagy még jobb, használhatjuk a bitenkéti operátort így nem kell végrehajtanunk a `-1`-es összehasonlítást.

# CoffeeScript string = "a long test string" included = !!~ string.indexOf "test" // JavaScript var included, string; string = "a long test string"; included = !!~string.indexOf("test");

Tulajdonság iteráció

Ahhoz, hogy végigiteráljunk a tulajdonságokon JavaScriptbe, az `in` operátort használjuk:

// JavaScript var object = {one: 1, two: 2} for(var key in object) alert(key + " = " + object[key])

Bár, ahogy az előző bekezdésben láthattuk, a CoffeeScript már lefoglalta ennek a kulcsszónak a használatát a tömbökre. Ezért az operátor átneveződött `of`-ra és a következő módon használhatjuk:

# CoffeeScript object = {one: 1, two: 2} alert("#{key} = #{value}") for key, value of object // JavaScript var key, object, value; object = { one: 1, two: 2 }; for (key in object) { value = object[key]; alert("" + key + " = " + value); }

Ahogyan láthattuk, megadhattuk a változókat a tulajdonság nevével és az értékével.

Min/Max

Ez a technikai nem kötődik specifikusan a CoffeeScripthez, de megemlítése mindenképpen hasznos. A `Math.max` és `Math.min` több argumentumot megenged így egyszerűen használtjuk a `...`-ot tömbök átadására, hogy megkapjuk a maximum és a minimum értékeket a tömbben.

# CoffeeScript Math.max [14, 35, -7, 46, 98]... # 98 Math.min [14, 35, -7, 46, 98]... # -7 // JavaScript Math.max.apply(Math, [14, 35, -7, 46, 98]); Math.min.apply(Math, [14, 35, -7, 46, 98]);

Érdemes megjegyezni, hogy ez a trükk hibára fut nagyon nagy tömbök esetén, mert a böngészők korláttal rendelkeznek a funkciónak átadható paraméterek számossága terén.

Többszörös argumentumok

A Math.max-os példában fent, a `...`-ot használtjuk, hogy szétszedjünk egy tömböt és többszörös argumentumként átadjuk a max-nak. A színfalak mögött, a CoffeeScript átalakítja a függvényhívást apply()-ra, ezzel biztosítva hogy a tömb argumentumokként adódik át. Ezt a funkciót másra is használhatjuk, úgy mint a függvény hívások proxyzása ill az argumentumokat megváltoztatására mielőtt továbbadódnak.

# CoffeeScript Log = log: -> console?.log(arguments...) // JavaScript var Log; Log = { log: function() { return typeof console !== "undefined" && console !== null ? console.log.apply(console, arguments) : void 0; } };

Vagy megváltoztatni az argumentumokat mielőtt továbbadódnak:

# CoffeeScript Log = logPrefix: "(App)" log: (args...) -> args.unshift(@logPrefix) if @logPrefix console?.log(args...) // JavaScript var Log; var __slice = Array.prototype.slice; Log = { logPrefix: "(App)", log: function() { var args; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; if (this.logPrefix) { args.unshift(this.logPrefix); } return typeof console !== "undefined" && console !== null ? console.log.apply(console, args) : void 0; } };

Ne feledje azonban, hogy a CoffeeScript automatikusan beállítja, a függvény hívás környezetét annak az objektumnak, amiből a függvény meglett hívva. A fenti példában, ez a `console` objektum. Ha szeretnénk a kontextust beállítani, akkor az meg kell hívnunk az apply() kézzel.

And/Or

CoffeeScript-ben a kódolási stílus azt mutatja, hogy az `or` előnyösebb a `||`- nél és az `and` előnyösebb a `&&`-nél, mert ezek jobban olvashatóbbak. Mindazonáltal, a két stílus ugyanolyan végeredménnyel jár. Ez a beszédszerűbb előny érvényesül, az `is`-nél a `==` felett és az `inst` az `!=` felett.

# CoffeeScript string = "migrating coconuts" string == string # true string is string # true // JavaScript var string; string = "migrating coconuts"; string === string; string === string;

Egy rendkívüli szép kiegészítés a CoffeeScript részéről a `or equals` kifejezése amit a Rubysták már ismerhetnek mint `||=`:

# CoffeeScript hash or= {} // JavaScript hash || (hash = {});

Ha a hash false-ra értékelődik ki, akkor beállítódik egy üres objektumra. Fontos megjegyezni, hogy ez a kifejezés a `0`, `””` és a `null`-t is false-nak ismeri fel. Ha nem ez a szándékunk, akkor a CoffeeScript egzisztenciális operátorát kell használnunk ,amely akkor aktiválódik amikor a hash `undefined` vagy `null`.

# CoffeeScript hash ?= {} // JavaScript if (typeof hash !== "undefined" && hash !== null) { hash; } else { hash = {}; };

Átrendező hozzárendelések

Az átrendező hozzárendeléseket bármilyen mélységben használhatjuk tömb vagy objektum egymásba ágyazott tulajdonságainak elérésénél.

# CoffeeScript someObject = { a: 'value for a', b: 'value for b' } { a, b } = someObject console.log "a is '#{a}', b is '#{b}'" // JavaScript var a, b, someObject; someObject = { a: 'value for a', b: 'value for b' }; a = someObject.a, b = someObject.b; console.log("a is '" + a + "', b is '" + b + "'");

Ez különösképp használható a Node alkalmazásokban modulok behúzásánál.

# CoffeeScript {join, resolve} = require('path') join('/Users', 'Alex') // JavaScript var join, resolve, _ref; _ref = require('path'), join = _ref.join, resolve = _ref.resolve; join('/Users', 'Alex');

Külső könyvtárak

Külső könyvtárak használata ugyanolyan egyszerű mint funkciókat meghívni a CoffeeScriptben könyvtáraknál; mivel a nap végén minden JavaScriptté fordítódik át. A CoffeeScript jQuery-vel együtt használva különösképp elegáns, a jQuery API callbackjainak számossága miatt.

# CoffeeScript # Use local alias $ = jQuery $ -> # DOMContentLoaded $(".el").click -> alert("Clicked!") // JavaScript var $; $ = jQuery; $(function() { return $(".el").click(function() { return alert("Clicked!"); }); });

Mivel minden CoffeeScript kimenet egy anonymous funkcióba ágyazódik be, beállíthatunk egy `$` álnevet a jQuery számára. Ezzel elérhetjük, hogy még a jQuery konfliktusmentes módja és a `$` átállításakor is a funkciók ugyanúgy működjenek ahogyan elterveztük.

Privát változók

CoffeeScriptben a do kulcsszóval futtathatunk egy funkciót azonnal, ami egy jó módszer a scope-ok egymásba ágyazására és a változók megvédésére. Az alábbi példában, bevezetjük a `classToType` változót egy anonymous funkció kontextusában amit azonnal meghívunk a `do`-val, Ez az anonymous funkció visszatér egy másik anonymous funkcióval ami végül a `type` végső értékét fogja tartalmazni. Ahogyan a `classToType`-ot egy olyan kontextusban vezettük be ahol nem rendeltük sehová a referenciát, nem lehet a scope-on kívülről elérni.

# CoffeeScript # Execute function immediately type = do -> classToType = {} for name in "Boolean Number String Function Array Date RegExp Undefined Null".split(" ") classToType["[object " + name + "]"] = name.toLowerCase() # Return a function (obj) -> strType = Object::toString.call(obj) classToType[strType] or "object" // JavaScript var type; type = (function() { var classToType, name, _i, _len, _ref; classToType = {}; _ref = "Boolean Number String Function Array Date RegExp Undefined Null".split(" "); for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; classToType["[object " + name + "]"] = name.toLowerCase(); } return function(obj) { var strType; strType = Object.prototype.toString.call(obj); return classToType[strType] || "object"; }; })();

Más szavakkal a `classToType` teljesen privát, és nem lehet soha többé hivatkozni a lefuttatott anonymous funkción kívül. Ez a minta jó a scope becsomagolás és a változó elrejtésére.