jashkenas/backbone — pull request #2683 by derickbailey (2013-07-19 15:09:27+00:00)
Title: Fix for `extend` to allow a type's `prototype.constructor` to be replaced

There are some scenarios where I (and others, like @brian-mann) need to replace the `prototype.constructor` function on a base view. The way the current `extend` method is implemented, though, causes a small problem and the replacement function isn't applied without additional workaround code.

This pull request fixes the problem by changing `parent.apply` to `parent.prototype.constructor.apply` in the `extend` method, when assigning the `child` variable to the anonymous function.

## Example Scenario: Automation ID

Say you have an application that always needs to set a specific data-attribute on view instances. You may need to supply an attribute like `data-automation-id` with a unique yet repeatable id for test automation. This id should only be available for test automation, though, and not in the production environment.

To facilitate this, you can add a new JavaScript file to a project and include code such as the following in it.

```js
// automationid.js
(function(){

// store a reference to the original constructor function
var originalConstructor = Backbone.View.prototype.constructor;

// create a function that can return a unique, repeatable id
function getAutomationId(view){

// do something here to get a unique, yet repeatable id
// based on the view instance that is passed in. send it
// back as a return value
var id = …;

return id;
}

// override the View's constructor
Backbone.View.prototype.constructor = function(options){

// call the original constructor function with
// the options provided to this instance
originalConstructor.call(this, options);

// set the automation id on the
var automationId = getAutomationId(this);
this.$el.data("automation-id", automationId);

// log a message, saying the automation-id is set
console.log("Automation ID set: ", automationId, view);
};

})();
```

In your project's HTML, include this file immediately after the backbone.js file. Putting it here will ensure that your automation id function is attached to the Backbone objects before anything like BBPlug is loaded.

```js

```

Now to test this and see the automation id.

### Testing The AutomationID Plugin

To test this, you only need to run an application after the `automationid.js` file has been included.

```js
(function(){

var V = BBPlug.ModelView.extend({
template: "#model-template"
});

$(function(){
var v = new V();
v.render();
$("#main").html(v.$el);
});

})();
```

Every view instance should log a message stating the automation id with the view instance. Only instead of seeing the message you expect, you will see nothing at all in the console logs.

![](http://cl.ly/image/0m2q2Q1d3l2F/empty-console.png)

This is definitely not what you wanted or expected. It should be showing the automation ids for each view, after all, and it isn't.

## The Problem: `parent.apply`

So why isn't it showing the automation id? Because the call to `parent.apply` doesn't account for the `parent.prototype.constructor` function having been replaced with something different.

This pull request fixes that.

## The Fix: `parent.prototype.constructor.apply`

After applying this change, you get the correct output in the console window:

![](http://cl.ly/image/292l0a1x3Y2A/automation-id-log.png)

## Current Workaround

Fortunately, there's a workaround for the current problem. When defining a type and you expect that the base type may have it's `prototype.constructor` function replaced, manually call it in your own constructor

```js
var MyView = Backbone.View.extend({
constructor: function(){
var args = Array.prototype.slice.apply(arguments);
Backbone.View.prototype.constructor.apply(this, args);
}
});
```

(I'm calling this the "super-constructor" pattern, FWIW)

## Code And Spec

This workaround shouldn't be necessary. The pull request includes a new spec to verify the functionality, and the needed change to make the spec pass.

This applies to every Backbone type, not just Views.