Skip to content

Instantly share code, notes, and snippets.

@aron
Last active January 4, 2016 00:49

Revisions

  1. aron revised this gist Jan 21, 2014. 1 changed file with 6 additions and 2 deletions.
    8 changes: 6 additions & 2 deletions constructors.md
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@ var MyView = Backbone.View.extend({
    });
    ```

    In the background this returning a throw-away constructor object to create the new instance, and then calling `.initialize` to handle setup. The up side of this is that we don't have to call the `Backbone.View` method with our new instance.
    In the background this returning a throw-away constructor object to create the new instance, and then calling `.initialize` to handle setup. The up-side of this, and the only reason I can see for it's existence, is that we don't have to call the parent `Backbone.View` method with our new instance.

    The downside to this is that it removes the benefits of having a named function as the constructor. Namely debugging and introspection.

    @@ -96,4 +96,8 @@ new MyView({model: SuperModel});
    //=> error: "MyView expected model option to be an AwesomeModel instance but received a SuperModel instance"
    ```

    This is something that can be introduced incrementally without disrupting the existing codebase. But will improve readability as it's introduced.
    So, I suggest that we phase out the use of `.initialize` and instead pass constructors. The functionality is the same, and all it requires is naming the constructor function and calling the parent constructor.

    Benefits are clearer code, the inheritance pattern is visible and consistent with extending other methods, and better visual output in browsers consoles that support it.

    This is something that can be introduced incrementally without disrupting the existing codebase. But will improve readability as it's introduced.
  2. aron created this gist Jan 21, 2014.
    99 changes: 99 additions & 0 deletions constructors.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,99 @@
    Using named constructors
    ------------------------

    The general usage of a Backbone class is to use the `.extend` method providing an `.initialize` method.

    ```js
    var MyView = Backbone.View.extend({
    initialize: function () {
    // Setup object.
    }
    });
    ```

    In the background this returning a throw-away constructor object to create the new instance, and then calling `.initialize` to handle setup. The up side of this is that we don't have to call the `Backbone.View` method with our new instance.

    The downside to this is that it removes the benefits of having a named function as the constructor. Namely debugging and introspection.

    ```
    var MyView = Backbone.View.extend({});
    MyView.name //=> ""
    new MyView().constructor.name //=> ""
    function MyOtherView() {
    Backbone.View.apply(this, arguments);
    }
    MyOtherView.name //=> "MyOtherView"
    new MyOtherView().constructor.name //=> "MyOtherView"
    ```

    Using the Chrome console to inspect objects is a common task, and without named constructors it can be confusing as to which type of object you are looking at.

    Take the following example:

    ```js
    var Car = Backbone.Model.extend({initialize: function () {}});
    var Bus = Backbone.Model.extend({initialize: function () {}});
    var Van = Backbone.Model.extend({initialize: function () {}});
    var VehicleCollection = Backbone.Collection.extend({initialize: function () {}});
    var GarageView = Backbone.View.extend({initialize: function () {}});

    var vehicles = new VehicleCollection();
    vehicles.add(new Car());
    vehicles.add(new Car());
    vehicles.add(new Bus());
    vehicles.add(new Van());

    var garageView = new GarageView({collection: vehicles});
    ```

    ![](http://cl.ly/TUN6/initialize.png)

    Backbone allows us to pass our own constructor into `.extend` and can be used in place of `.initialize` and is closer to a bare JavaScript inheritance style. This gives us much clearer output in the Chrome console.

    ```js
    var Car = Backbone.Model.extend({constructor: function Car() {
    Backbone.Model.apply(this, arguments);
    }});
    var Bus = Backbone.Model.extend({constructor: function Bus() {
    Backbone.Model.apply(this, arguments);
    }});
    var Van = Backbone.Model.extend({constructor: function Van(){
    Backbone.Model.apply(this, arguments);
    }});
    var VehicleCollection = Backbone.Collection.extend({constructor: function VehicleCollection() {
    Backbone.Collection.apply(this, arguments);
    }});
    var GarageView = Backbone.View.extend({constructor: function GarageView() {
    Backbone.View.apply(this, arguments);
    }});

    var vehicles = new VehicleCollection();
    vehicles.add(new Car());
    vehicles.add(new Car());
    vehicles.add(new Bus());
    vehicles.add(new Van());

    var garageView = new GarageView({collection: vehicles});
    ```

    ![](http://cl.ly/TUaF/constructors.png)

    This also allows us to provide more debugging information when passed the incorrect data. For example if we require a specific model type to be passed into a view we can use the name property to provide useful feedback to the developer.

    ```js
    constructor: function MyView(options) {
    Backbone.View.apply(this, arguments);

    if (!(options.model instanceof AwesomeModel)) {
    var modelName = options.model.name || "unknown";
    throw new Error("MyView expected model option to be an AwesomeModel instance but received a " + modelName + " instance");
    }
    }

    // Later
    new MyView({model: SuperModel});
    //=> error: "MyView expected model option to be an AwesomeModel instance but received a SuperModel instance"
    ```

    This is something that can be introduced incrementally without disrupting the existing codebase. But will improve readability as it's introduced.