[javascript] Perché CoffeeScript esegue il wrapping delle definizioni di classe in una chiusura?



1 Answers

Un altro motivo per avvolgere la definizione della classe con una chiusura è di dare a quel codice un nuovo scope lessicale per dichiarare variabili e cose che sono visibili solo all'interno della classe:

class AwesomeThing
  # You might have private "methods" here.
  doSomethingAwesome = (what) ->
    console.log "I'm doing #{what} like a pro!"
  # Or run any arbitrary code.
  for i in [1..10]
    @prototype["uselessMethod#{i}"] = -> 'nothing'

  beAwesome: ->
    doSomethingAwesome @uselessMethod5() # The 5'th useless method is the best.

In quel codice, le variabili doSomethingAwesome e i sono locali alla definizione della classe, rendendoli così "privati" agli utenti esterni.

Il compilatore CoffeeScript potrebbe rimuovere il wrapping extra se non sono necessarie variabili locali IMO. Ma avvolgere sempre la definizione della classe è probabilmente più semplice da una prospettiva di implementazione :)

Question

In CoffeeScript, questo :

class Foo
  method: (x) ->
    x+1

Compilati a:

// Generated By CoffeeScript
Foo = (function() {
  function Foo() {}
  Foo.prototype.method = function(x) {
    return x+1;
  }
  return Foo;
})()

Che sembra un po 'eccessivo. Il seguente dovrebbe essere funzionalmente identico :

// Generated by Dave
function Foo() {}
Foo.prototype.method = function(x) {
    return x+1;
}

Qual è la motivazione per il wrapper extra "chiusura"?

Questa non è una semplice questione di stile; ha implicazioni sulla dimensione complessiva del codice.

La versione di Coffee si riduce a 84 byte:

Foo=function(){function e(){}return e.prototype.method=function(e){return e+1},e}();

La mia versione si riduce a soli 61 byte:

function Foo(){}Foo.prototype.method=function(e){return e+1};

23 byte sono tipi sciocchi di irrilevante, ma in un progetto con molte molte classi, il sovraccarico comincia a sommarsi.

Ok, ho scritto una risposta qui sotto confutando la teoria della dimensione dei byte ... per ogni classe ragionevole, il metodo Coffee sarà più piccolo.

Probabilmente ci sono anche altri motivi. Aiutami a pensare a loro.




OK, penso di aver appena risposto alla mia stessa domanda.

Per la maggior parte delle classi ragionevoli, la chiusura generata da CoffeeScript genera output minified più piccoli.

Il wrapper di chiusura è di 25 byte di overhead minorato, ma ti salva dalla ripetizione del nome della classe , risparmiando k * N byte (k = lettere-in-nome, N = num-of-refs). ad esempio, se una classe come BoilerPlateThingyFactory ha 2+ metodi, il wrapper di chiusura genera un codice BoilerPlateThingyFactory più piccolo.


più in dettaglio...

Il codice generato da Coffee che utilizza una chiusura riduce a:

// Uglify '1.js' = 138 bytes (197 w/ whitespace):

var Animal=function(){function e(e){this.name=e}return e.prototype.speak=function(e){return"My name is "+this.name+" and I like "+e},e}();

// with whitespace ("uglifyjs -b"):

var Animal = function() {
    function e(e) {
        this.name = e;
    }
    return e.prototype.speak = function(e) {
        return "My name is " + this.name + " and I like " + e;
    }, e;
}();

L'implementazione alternativa riduce a questo:

// Uglify '2.js' = 119 bytes (150 w/ whitespace):

var Animal=function(t){this.name=t};Animal.prototype.speak=function(e){return"My name is "+this.name+" and I like "+e};

// with whitespace ("uglifyjs -b"):

var Animal = function(t) {
    this.name = t;
};

Animal.prototype.speak = function(e) {
    return "My name is " + this.name + " and I like " + e;
};

Si noti come il nome "Animale" appaia esattamente una volta nella forma Caffè e N = 2 volte nell'implementazione alternativa. Ora "Animale" ha solo 6 lettere, e c'è solo 1 metodo, quindi Coffee qui dovrebbe perdere di 25-6 = 19 byte. Consultando il mio codice miniato, è 138 byte a 119 byte, per un delta di ... 19 byte. Aggiungi altri 4 metodi e il vantaggio passerà a Coffee. E non sono solo i metodi; anche le costanti di classe e altri tipi di ref.




Related