-
-
Save aroc/5715154 to your computer and use it in GitHub Desktop.
// 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; | |
}); |
I forgot to mention the main point, that the fiddle shows that an instance of SignupModel
and instance of UserModel
have independent validation
values. In your code, I'm unsure how setting this.validation
in initialize
has any effect outside of that instance of SignupModel
.
How are you creating SignupModel
instances? Are you using call
, apply
, or _.bind
to create SignupModel
instances? The only way I can think of that would cause assigning to this.validation
to affect UserModel
is by supply UserModel.prototype
as this
via one of those functions. Maybe another possibility is forgetting to invoke with new
.
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.
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.
@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.
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
Ya @kastiglione that should work. The issue with the previous solutions is that we are needing a deep copy of the validations object.
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.
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.
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
@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.
With the code you've shown, it's mixing prototype properties and instance properties.
UserModel
has avalidation
property on its prototype, whileSignupModel
does not. Instances ofUserModel
do not have an instance property forvalidation
, instead getting their value fromUserModel
's prototype. Instances ofSignupModel
do have an instance property forvalidation
, which means the prototype chain is not referenced.Here's a fiddle showing what I mean, open the console to see the output.
http://jsfiddle.net/Lt9kz/4/