A CoffeeScript programozási nyelv

Osztályok, öröklés

Osztály, öröklés, tagok

A JavaScript nyelvben nincsenek osztályok és öröklődés, csak prototípusok vannak. A JavaScript-hez létezik jónéhány könyvtár amely megpróbálja megvalósítani az osztály és az öröklés fogalmát, de ezekkel mindig az a probléma, hogy nehézkes hozzáférni a szülő prototípushoz, nincs "super" fogalom. A CoffeeScript-ben kiegészítették a nyelvet a class kulcsszóval az örökléssel (extends kulcsszó) és a super fogalommal amely megegyezik a Java super kifejezésével.

Minden osztálynak legfeljebb egy őse lehet, láthatóságot nem lehet megadni, minden publikus. Az osztály törzsét a behúzás határozza meg. A tagok megadása a következő szintaktikával megy: "tag: kif". Egy speciális tag a constructor. Ez mint a neve is mutatja egy olyan függvényt jelöl, amely konstruktorként lesz majd használva a példányosításkor. Legfeljebb egy konstruktort lehet definiálni.

Példányosítás

Az osztályokat a new kulcsszóval lehet példányosítani. A new kulcsszó után meg kell adni az osztály nevét, majd meg kell adni a konstruktor paramétereit (ha van). Az eredmény egy új objektum lesz. Erre az előző kódrészletben lehet példát találni.

Alacsony-szintű prototípus hozzáférés

A JavaScript hozzáférést biztosít az objektumok prototípusához és ezt az alacsony-szintű hozzáférést meg akarták tartani a CoffeeScript-ben is. Erre vezették be a :: operátort amit a következő szintaktikával használhatunk: "osztály::tag = kif". Az alábbi példával talán könnyebben érthető:

String::dasherize = -> this.replace /_/g, "-"

A JavaScriptbeli osztály fogalma elsőre nagyon furcsának tűnik és a megvalósítása bonyolult mintákat követ. A nyelv maga közvetlenül nem támogatja az osztály szemléletet. Mégis, úgy tűnik, hogy a konstruált osztályok igen hasznosak a JavaScriptben ugyanúgy mint a többi nyelvben.

A színfalak mögött a CoffeeScript a JavaScript beépített prototípusait használja hogy osztályokat készítsen; egy kevéske szintaktikai cukorkát adva a statikus tulajdonság örökléshez és kontextus perzisztenciához. Fejlesztőként ebből csak a "class" kulcsszó látszik.

# CoffeeScript class Animal // JavaScript var Animal; Animal = (function() { function Animal() {} return Animal; })();

A fenti példában, az Animal az osztály és az eredő változónak is a neve amit a példányosításkor használhatunk. A színfalak mögött a CoffeeScript konstruktor eljárásokat használ. Osztályokat a new kulcsszóval lehet példányosítani.

# CoffeeScript animal = new Animal // JavaScript var animal; animal = new Animal;

Konstruktort (olyan funkció ami a példányosításkor hívódnak meg) definiálni egyszerű, csak a "constuctor" nevű függvényt kell használnunk. Ez rokon a Ruby initalize-vel vagy a Python __init__-jével.

# CoffeeScript class Animal constructor: (name) -> @name = name // JavaScript var Animal; Animal = (function() { function Animal(name) { this.name = name; } return Animal; })();

A CoffeeScript rövidítéseket használ a példányváltozók beállításának általános mintájára. Az argumentumot a @-jellel prefixálva, a CoffeeScript automatikusan beállítja a konstruktorban mint példánytulajdonságot. Tulajdonképpen ez a rövidítés működik a normál eljárásoknál is, osztályokon kívül. A példa alul megegyezik az utóbbi példával, ahol manuálisan állítottuk be a példány tulajdonságát.

# CoffeeScript class Animal constructor: (@name) -> // JavaScript var Animal; Animal = (function() { function Animal(name) { this.name = name; } return Animal; })();

Ahogy azt már várható, inicalizáláskor mindegyik argumentum az átadódik a konstruktornak.

# CoffeeScript animal = new Animal("Parrot") alert "Animal is a #{animal.name}" // JavaScript var animal; animal = new Animal("Parrot"); alert("Animal is a " + animal.name);

Példány tulajdonságok

További példány tulajdonságok hozzáadása az osztályhoz nagyon egyszerű, pontosan ugyanaz a szintaxis mintha tulajdonságot adnánk az objektumhoz. Csak bizonyosodjunk meg abban, hogy a tulajdonságok helyesen szerepelnek az osztály törzsében.

# CoffeeScript class Animal price: 5 sell: (customer) -> animal = new Animal animal.sell(new Customer) // JavaScript var Animal, animal; Animal = (function() { function Animal() {} Animal.prototype.price = 5; Animal.prototype.sell = function(customer) {}; return Animal; })(); animal = new Animal; animal.sell(new Customer);

A kontextusok váltásai JavaScriptben mindennaposak, ahogy azt a szintaxisoknál már beszéltük. Az adott kontextust a kettős nyíl funkció jelöléssel őrizzük meg. Ez biztosítja azt, hogy bármilyen kontextusból is hívtuk meg a függvényünket, mindig abban a kontextusban fog futni amelyben az definiálva lett. A CoffeeScript kitüntetett támogatással rendelkezik a kettős nyilakkal az osztályokban. A kettős nyilak használata a példány eljárásokban biztosít minket, hogy mindig a helyes kontextusban fog meghívódni a függvény.

# CoffeeScript class Animal price: 5 sell: => alert "Give me #{@price} shillings!" animal = new Animal $("#sell").click(animal.sell) // JavaScript var Animal, animal; var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; Animal = (function() { function Animal() { this.sell = __bind(this.sell, this); } Animal.prototype.price = 5; Animal.prototype.sell = function() { return alert("Give me " + this.price + " shillings!"); }; return Animal; })(); animal = new Animal; $("#sell").click(animal.sell);

Ahogy az előbbi példában demonstráltuk, ez különösképp az események callback-jainál hasznos. Normális esetben a sell() függvény a #sell element kontextusában hívódna meg. Viszont, kettős nyilat használva a sell() függvénynél, biztosítjuk hogy a helyes kontextus megmarad és a this.price értéke 5-lesz.

Statikus tulajdonságok

Mi a helyzet az osztály (statikus) tulajdonságokkal? Nos, ahogy azt már láttuk, az osztály definíciójában ez az osztály objektumra mutat. Más szavakkal, osztály tulajdonságokat úgy állíthatunk be ha közvetlenül ezen objektumon állítjuk be.

# CoffeeScript class Animal this.find = (name) -> Animal.find("Parrot") // JavaScript var Animal; Animal = (function() { function Animal() {} Animal.find = function(name) {}; return Animal; })(); Animal.find("Parrot");

Tulajdonképpen, ahogy emlékezhetünk, a CoffeeScript a this-re a @ álnevet használja, amely statikus tulajdonságokat még tömörebben írhatunk.

# CoffeeScript class Animal @find: (name) -> Animal.find("Parrot") // JavaScript var Animal; Animal = (function() { function Animal() {} Animal.find = function(name) {}; return Animal; })(); Animal.find("Parrot");

Öröklődés & super

A programozási nyelvekben az osztályok fogalmak nem lenne teljes az öröklődés támogatása nélkül és ebben a CoffeeScript nem okoz csalódást. Az extends kulcsszóval származtathatunk egy másik osztályból A fenti példában, a "Parrot" kiterjeszti az "Animal"-t, minden példány tulajdonságát örökölve, mint például az alive() függvényt.

# CoffeeScript class Animal constructor: (@name) -> alive: -> false class Parrot extends Animal constructor: -> super("Parrot") dead: -> not @alive() // JavaScript var Animal, Parrot; var __hasProp = Object.prototype.hasOwnProperty, __extends = 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; }; Animal = (function() { function Animal(name) { this.name = name; } Animal.prototype.alive = function() { return false; }; return Animal; })(); Parrot = (function() { __extends(Parrot, Animal); function Parrot() { Parrot.__super__.constructor.call(this, "Parrot"); } Parrot.prototype.dead = function() { return !this.alive(); }; return Parrot; })();

A fent példában felfigyelhetünk a super() kulcsszó használatára. A színfalak mögött, ez az osztály szülő prototípusának funkcióhívására fordítódik át, a helyi kontextusból meghívva. Ebben az esetben, a Parrot.__super__.constructor.call(this, "Parrot") hívódik meg; A gyakorlatban, pontosan ugyanaz történik, mint amikor a supert hívjuk meg Ruby-ban vagy Pythonban: meghívódik a felülírt örökölt funkció.

Hacsak nem írjuk fölül a konstruktort, a CoffeeScript alapértelmezetten meghívja a szülő konstruktorát amikor a példány elkészül.

A CoffeeScript prototípus másolást használ az osztály példány tulajdonságainak automatikus öröklésére. Ez azt a működést biztosítja, hogy az osztályok dinamikusak: még ha egy új tulajdonságot is adunk a szülő osztályhoz miután a gyermek elkészült, a tulajdonság propagálva lesz az összes származtatott gyermekhez.

# CoffeeScript class Animal constructor: (@name) -> class Parrot extends Animal Animal::rip = true parrot = new Parrot("Macaw") alert("This parrot is no more") if parrot.rip // JavaScript var Animal, Parrot, parrot; var __hasProp = Object.prototype.hasOwnProperty, __extends = 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; }; Animal = (function() { function Animal(name) { this.name = name; } return Animal; })(); Parrot = (function() { __extends(Parrot, Animal); function Parrot() { Parrot.__super__.constructor.apply(this, arguments); } return Parrot; })(); Animal.prototype.rip = true; parrot = new Parrot("Macaw"); if (parrot.rip) { alert("This parrot is no more"); }

Érdemes rámutatni, hogy a statikus tulajdonságok inkább másolódnak az alosztályokba, mintsem öröklődnének. Ez a JavaScript prototípus architektúrának implementációija miatt van, és ez egy nehéz probléma.