diff --git a/backbone.js b/backbone.js index 5bfd974cd..9d0de9b74 100644 --- a/backbone.js +++ b/backbone.js @@ -2107,9 +2107,16 @@ // The constructor function for the new subclass is either defined by you // (the "constructor" property in your `extend` definition), or defaulted - // by us to simply call the parent constructor. + // by us to simply call the parent constructor. ES6 method shorthand + // (`constructor() {}`) produces a non-constructable function with no + // `prototype`; in that case we wrap it so `new` still works. if (protoProps && _.has(protoProps, 'constructor')) { - child = protoProps.constructor; + var supplied = protoProps.constructor; + if (supplied.prototype) { + child = supplied; + } else { + child = function(){ return supplied.apply(this, arguments); }; + } } else { child = function(){ return parent.apply(this, arguments); }; } diff --git a/test/model.js b/test/model.js index 23d7d66bd..8123ff0f1 100644 --- a/test/model.js +++ b/test/model.js @@ -93,6 +93,28 @@ assert.notEqual(model.cid, undefined); }); + QUnit.test('extend supports ES6 method-shorthand constructor (#4213)', function(assert) { + assert.expect(2); + // ES6 method shorthand (`constructor() {}`) produces a non-constructable + // function with no `prototype` property, which previously made + // `new Model()` throw "Model is not a constructor". We use `eval` here + // so this file still parses on engines without ES6 method shorthand. + var protoProps; + try { + /* eslint-disable no-eval */ + protoProps = eval('({ constructor() { Backbone.Model.apply(this, arguments); } })'); + /* eslint-enable no-eval */ + } catch (e) { + assert.ok(true, 'ES6 method shorthand not supported in this environment, skipping'); + assert.ok(true); + return; + } + var Model = Backbone.Model.extend(protoProps); + var model = new Model({foo: 'bar'}); + assert.ok(model instanceof Model); + assert.equal(model.get('foo'), 'bar'); + }); + QUnit.test('parse can return null', function(assert) { assert.expect(1); var Model = Backbone.Model.extend({