Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Coffeescript class extend more bloat than Backbone extend

I am just beginning to learn Coffeescript and cannot find a definite answer to why I should use

class Model extends Backbone.Model
    urlRoot: '//some/url'

compiles to

Model = (function(_super) {
    __extends(Model, _super);

    function Model() {
        _ref = Model.__super__.constructor.apply(this, arguments);
        return _ref;
    }

    Model.prototype.urlRoot = '//some/url';

    return Model;

})(Backbone.Model);

as opposed to

Model = Backbone.Model.extend
    urlRoot: '//some/url'

compiles to

var Model = Backbone.Model.extend({
    urlRoot: '//some/url'
});

The main reason I am asking is because the former is used in nearly all examples I have looked at. However, it creates 'much' more bloat when compiled as opposed to the latter. I did read this question, but the answers seem to differ.

like image 257
TYRONEMICHAEL Avatar asked Aug 13 '13 06:08

TYRONEMICHAEL


1 Answers

Since you're asking just about the bloat, let's have a look at some code.

JavaScript with Backbone.Model.extend

If you open up the Backbone source code, you'll see the extend function is the following:

var extend = function(protoProps, staticProps) {
    var parent = this;
    var child;

    if (protoProps && _.has(protoProps, 'constructor')) { // _.has comes from
      child = protoProps.constructor;                     // underscore, even 
    } else {                                              // more 'bloat'
      child = function(){ return parent.apply(this, arguments); };
    }

    _.extend(child, parent, staticProps);                // more underscore

    var Surrogate = function(){ this.constructor = child; };
    Surrogate.prototype = parent.prototype;
    child.prototype = new Surrogate;

    if (protoProps) _.extend(child.prototype, protoProps);

    child.__super__ = parent.prototype;

    return child;
  };

What actually happens here:

When we call

var Model = Backbone.Model.extend({urlRoot: '//some/url' });

we get something like:

  // Create new constructor which calls the parent constructor
  var Model;
  if (({}).hasOwnProperty.call({urlRoot: '//some/url' }, 'constructor') {
      // this is false so...                    
  } else {
      Model = function(){ return Backbone.Model.apply(this, arguments); };
  }

  // Set up prototype chain
  var Surrogate = function(){ this.constructor = model; };
  Surrogate.prototype = Backbone.Model.prototype;
  Model.prototype = new Surrogate;

  // Add properties to the child prototype
  // Same as:
  // Model.prototype.urlRoot = '//some/url';
  _.extend(Model.prototype, { urlRoot: '//some/url' });

  // Set the magical __super__ property
  Model.__super__ = Backbone.Model.prototype;

CoffeeScript with extends

Compare that with the CoffeeScript code. You will see that when you use extends a magical function called __extends gets added to the start of your file, which (when formatted) looks like:

__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; 
};

which is combined with the generated JS:

var Model = (function(_super) {
    __extends(Model, _super);

    function Model() {
        _ref = Model.__super__.constructor.apply(this, arguments);
        return _ref;
    }

    Model.prototype.urlRoot = '//some/url';

    return Model;

})(Backbone.Model);

What actually happens here:

When we call

Model extends Backbone.Model
    urlRoot: '//some/url'

we get something like:

// Create new constructor which calls the parent constructor
var Model = function () {
    return Model.__super__.constructor.apply(this, arguments);
}

// Copy static properties from Backbone.Model to Model
for (var key in Backbone.Model) {
    if (__hasProp.call(Backbone.Model, key)) 
        Model[key] = Backbone.Model[key]; 
}

// Set up prototype chain
function ctor() { this.constructor = Model; } 
ctor.prototype = Backbone.Model.prototype; 
Model.prototype = new ctor(); 

// Add properties to the child prototype
Model.prototype.urlRoot = '//some/url';

// Set the magical __super__ property
Model.__super__ = Backbone.Model.prototype; 

What do we see?

They look pretty similar don't they?

CoffeeScript is just JavaScript. If you are already using Backbone and want to avoid adding in the __extends function in your generated source, then use Backbone.Model.extend. If you want to avoid adding in Backbone all together then extends does practically the same thing. The reason that so many examples don't use the latter is that Backbone is not required to use CoffeeScript - it just wouldn't make sense to have an example which relies on an external library.

like image 97
phenomnomnominal Avatar answered Jan 29 '23 00:01

phenomnomnominal