Why does coffeescript generate classes like this?
Given the following coffeescript code:
class Animal
constructor: (@name) ->
speak: (things) -> "My name is #{@name} and I 开发者_如何学Clike #{things}"
This is generated:
var Animal = (function() {
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function(things) {
return "My name is " + this.name + " and I like " + things;
};
return Animal;
})();
But why isn't this more idiomatic code generated?
var Animal = function Animal(name) {
this.name = name;
};
Animal.prototype.speak = function(things) {
return "My name is " + this.name + " and I like " + things;
};
I know that coffeescript wraps a lot of stuff in anonymous functions to control scope leak, but what could leak here?
The generated code makes it possible to reliably have named functions in Internet Explorer. (In this case, "Animal".) If you simply use a named function at top-level scope, it will conflict with any var Animal =
declarations that might be present ... even in lower scopes, preventing them from being referenced correctly. To work around the IE bug, we include the function wrapper around the class definition.
This is to support backtraces including the class names and not just the function names when an exception is thrown.
The CoffeeScript method also has advantages for minification.
From my other answer:
For most reasonable classes, the closure generated by CoffeeScript generates smaller minified output.
The closure wrapper is 25 bytes of minified overhead, but it saves you from repeating the classname, saving k * N
bytes (k=letters-in-name, N=num-of-refs).
e.g., if a class like BoilerPlateThingyFactory
has 2+ methods, the closure wrapper generates smaller minified code.
in more detail...
The Coffee generated code using a closure minifies to:
// 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;
}();
ryeguy's alternative "idiomatic" implementation minifies to this:
// 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;
};
Notice how the name "Animal" name exists precisely once in the Coffee form, and N=2 times in ryeguy's "idiomatic" varient. Now "Animal" is only 6-letters, and there's only 1 method, so Coffee here should lose by 25-6 = 19 bytes. Consulting my minified code, it's 138 bytes to 119 bytes, for a delta of ... 19 bytes . Add 4 more methods, and the advantage will switch to Coffee. And it's not just methods; class constants and other ref types count too.
精彩评论