最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Coffeescript class extend more bloat than Backbone extend - Stack Overflow

programmeradmin5浏览0评论

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.

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.

Share Improve this question edited May 23, 2017 at 12:07 CommunityBot 11 silver badge asked Aug 13, 2013 at 6:36 TYRONEMICHAELTYRONEMICHAEL 4,2444 gold badges31 silver badges48 bronze badges 4
  • What exactly do you think "bloat" is? Why do you think it's bad? If you're talking about verbosity of code then... Why? It's generated code, it doesn't matter. – user229044 Commented Aug 13, 2013 at 12:53
  • 1 @meagar, well if you writing a large scale app with many models, collections and views, the generated code definitely needs to matter. Also I would like the generated code to be easily read by those who have not yet learnt Coffeescript. – TYRONEMICHAEL Commented Aug 13, 2013 at 18:03
  • 2 @TyroneMichael No, it really doesn't, any more than the generated binary matters in a modern desktop application. – user229044 Commented Aug 13, 2013 at 19:09
  • 1 Looking at the results of these, the difference is about 17 bytes per class once run through a minifier. In a (fairly large) app I have about 45 classes, which equates to 765 bytes. Compare this to the total of 140,000 bytes it currently is. Worrying about this is a micro-optimization at best. – James Kyle Commented Jun 9, 2014 at 2:15
Add a comment  | 

1 Answer 1

Reset to default 26 +300

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.

发布评论

评论列表(0)

  1. 暂无评论