Coffeescript “sintaxis sugar” para javascript.

Coffeescript es un lenguaje que no gusta a algunos pero a otros nos apasiona. En este artículo asumo que sabes javascript, has programado javascript, te has peleado con él, si no, os recomiendo encarecidamente que dediqueis a profundizar en él, ya que al final coffeescript solo es javascript, y muchas de las cosas que hacen a coffeescript tan útil es limar sus imperfecciones, y permitirnos hacer lo mismo que nos promete jquery, write less, do more.

Cuando hablamos de coffeescript como sintaxis sugar, tenemos que darnos cuenta de que simplemente estamos escribiendo javascript, meta programado, eso si. Veamoslo con ejemplo muy sencillo

console.log("Hola, mundo!");

esta linea es perfectamente válida tanto en javascript como en coffeescript, pero en coffeescript podemos prescindir del ; y de los paréntesis

console.log "Hola, mundo!"

Parece poco ahorro, pero, cuando haces printf debugging, se agradece. Si este fuera el único ahorro que nos propone coffeescript, probablemente no os estaría hablando de él. Uno de los aspectos que nos ahorran una cantidad sustancial de código cuando escribimos aplicaciones en coffeescript son sus opciones de OO, la palabra reservada class encierra muchísimas líneas de código de ahorro, ademas de una sintaxis limpia y muy accesible. Veamos un pequeño ejemplo.

class Demo
  foo: ->
    console.log 'foo drom Demo'

class DemoII extends Demo
  foo: ->
    super
    console.log 'foo from Demo II'

Un código conciso y muy fácil de entender si conocemos principios de OOP. En javascript esto no es posible, no existe el concepto de clase, menos de extenderlas y del super ni te cuento. Para simular todo esto, coffeescript tiene que añadirnos mucho código.

// Generated by CoffeeScript 1.3.1
(function() {
  var Demo, DemoII,
    __hasProp = {}.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; };

  Demo = (function() {

    Demo.name = 'Demo';

    function Demo() {}

    Demo.prototype.foo = function() {
      return console.log('foo drom Demo');
    };

    return Demo;

  })();

  DemoII = (function(_super) {

    __extends(DemoII, _super);

    DemoII.name = 'DemoII';

    function DemoII() {
      return DemoII.__super__.constructor.apply(this, arguments);
    }

    DemoII.prototype.foo = function() {
      DemoII.__super__.foo.apply(this, arguments);
      return console.log('foo from Demo II');
    };

    return DemoII;

  })(Demo);

  new DemoII().foo();

}).call(this);

Podríamos usar alguna librería que nos ayudara a simular clases y herencia desde el mismo javascript, como klass o Mootools, pero se agradece tener clases en el lenguaje y no en un API. Coffeescript nos ofrece una sintaxis ligera para OOP, pero tenemos que entender que coffeescript es javascript, con todo lo que ello conlleva. Cuando trabajamos con objetos en javascript una de las cosas complicadas de manejar es this, veamos lo con otro pequeño ejemplo ahora en javascript

var demo = {
  kk:10,
  fn:function(){
    console.log(this.kk);
  }
}

demo.fn(); // 10

Este ejemplo funciona como esperamos. Al llamar a la función con la construcción vista, javascript nos engancha demo al contexto this de la función. El problema viene, cuando no somos nosotros quienes llamamos a la función, si no que se la pasamos a alguien para que la llame. Simplificando y solo para mostrar el tema podemos verlo como:

var demo = {
  kk:10,
  fn:function(){
    console.log(this.kk);
  }
}
var fn_later = demo.fn
fn_later() // undefined

Ahora la cosa no va tan bien, cuando llamamos a la función no sabemos nada del objeto que la contiene, con lo que no puede enseñarnos el valor que esperaríamos. Soluciones para esto, hay muchas:

var demo = {
  kk:10,
  fn:function(){
    console.log(this.kk); // usar demo en vez de this, funcional aunque poco
  }                       // DRY si es un objeto tipo singleton
}
var fn_later = demo.fn
fn_later()
fn_later.call(demo) // establecer directamente demo como contexto cuando
                    // llamamos a la función

Y algunas más elaboradas. Pero nosotros estamos hablando de coffeescript y este tiene una característica muy interesante, que son las flechas gordas, “fat arrows”, => , que lo que hacen es que nuestra función ya se encuentre vinculada al propio objeto:

class Demo
  val: 10
  no_bind: ->
    console.log @val # this.val
  binded: =>
    console.log @val

demo = new Demo()
demo.no_bind() # 10
demo.binded() # 10

fn_no_bind = demo.no_bind
fn_binded = demo.binded

fn_no_bind() # undefined
fn_binded() # 10

Si miráis el código traducido, comprobaréis que lo que hace es bindear la función en el constructor al propio contexto, pero en coffeescript es rápido, conciso y nuevamente muy legible.

Coffeescript trae otras características tambien muy agradecidas, como expansión de cadenas, construcciones ‘for in’ o ‘for of’ y otras más, pero tenemos que tener presente siempre, esto es javascript, y es muy importante tenerlo en cuenta, ya que cuando estemos arreglando problemas en tiempo de ejecución lo estaremos haciendo en javascript y nunca en coffeescript, si este falla será por un problema de sintaxis, y no podrá ejecutarse, pero una vez pasado a javascript tendremos que saber de éste.

Happy coffee coding!.