Skip to content

Instantly share code, notes, and snippets.

@aroc
Created June 5, 2013 16:14
Show Gist options
  • Save aroc/5715154 to your computer and use it in GitHub Desktop.
Save aroc/5715154 to your computer and use it in GitHub Desktop.
Factory Pattern for Backbone.js
// Allows you to create a child which is a copy of the parent and changing the child or parent doesn't affect one another.
// Calling "this" inside the child won't change attributes of the parent, as it is a copy and not connected through the prototype chain.
define(['jquery', 'underscore', 'backbone'], function ($, _, Backbone) {
// NOTE: This works in every scenario I've tested it in. However, this code looks bad
// and there may very well be a much cleaner + better way to handle this. Or maybe not
// because JS prototypes are crazy.
function childOfClass (parentFunction, newAttributes) {
// Create the new function and ensure to call Backbone's standard constructor
// with the context of *this* (this function).
// NOTE: We could change this to be the constructor of the parentFunction in order to
// have the parentFunction constructor code ran, then add in a call to
// this.initialize.apply(this, arguments) to *also* run this function's initialize method upon
// object construction.
function childFunction () {
Backbone.Model.prototype.constructor.apply(this, arguments);
}
// Set the prototype equal to that of a NEW parentFunction. We do new
// parentFUnction instead of just parentFunction or parentFunction.prototype
// because this breaks the prototype chain and allows us to inherit a *copy* of the
// parentFunction's attributes.
childFunction.prototype = new parentFunction();
// After we've set the prototype equal to that of the parentFunction, ensure
// the constructor is set to the childFunction function, or the Backbone.Model
// constructor code won't get run when the object is created.
childFunction.prototype.constructor = childFunction;
// Extend the prototype with the new / additional functionality.
_.extend(childFunction.prototype, newAttributes);
// Return the new function / class.
return childFunction;
}
return childOfClass;
});
@percyhanna
Copy link

I'm also not seeing how this would break, unless you're not using the new keyword. SignupModel.prototype should inherit validation from UserModel's prototype. But setting this.validation in the constructor on SignupModel should only change the value on that instance, not either of the prototypes.

Are you using call or apply anywhere to change the value of this used when your constructor is called? That's what @kastiglione is getting at.

@aroc
Copy link
Author

aroc commented Jun 6, 2013

I've come across my issue. I want SignupModel to inherit values from UserModel, then I want to be able to directly modify those values while keeping a separate copy. This jsfiddle outlines precisely my issue.

http://jsfiddle.net/Lt9kz/5/

@percyhanna
Copy link

@aroc: That's exactly what I was referring to. You're using a Hash/Object in the UserModel prototype, and you're just changing a single value in the child class. Both classes are sharing the same instance of the Hash/Object.

@kastiglione
Copy link

I haven't tried it, but try adding this.validation = $.extend(true, {}, this.validation) to get a local deep copy of the validation hash.

http://api.jquery.com/jQuery.extend/#jQuery-extend-deep-target-object1-objectN

@ekryski
Copy link

ekryski commented Jun 6, 2013

Ya @kastiglione that should work. The issue with the previous solutions is that we are needing a deep copy of the validations object.

@aroc
Copy link
Author

aroc commented Jun 6, 2013

We tried that here a couple days ago. It does work, but the problem is I need to deep copy every attribute I want to from UserModel over to SignupModel. I'd prefer if I didn't need to go about it that way.

We tried doing a deep copy of the UserModel's prototype, but couldn't manage to get it working properly - it still got overridden.

@tylermercier
Copy link

The issue is that hasOwnProperty will only look at the immediate object and won't navigate the prototype chain. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
hasOwnProperty is only good for telling where the function/object is defined, not if the object will respond to the function. For that I would call it and see if you get 'undefined', unless there is a better way.

Add the following to your fiddle and you'll see it works fine.

alert((new UserModel).validation['user']['type']);
alert((new SignupModel).validation['user']['type']);

Unless I am missing something from your fiddle.

@tylermercier
Copy link

The other thing to be aware of is that it appears Backbone defines it's own extend function. If you are looking at the one in underscore, you are looking in the wrong place. It's at the bottom of the page. http://backbonejs.org/docs/backbone.html

@kastiglione
Copy link

@tylermercier The parts in the gist about hasOwnProperty were from me, but they're irrelevant. See the last two comments by @ekryski and @aroc. Basically, they want deep copying of a number of properties from the parent's prototype, but they don't want to explicitly do that in each subclass initializer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment