Reputation: 920
I am programming with Backbone.js in CoffeeScript and have a problem with inheritance in relation with lodash and the merge function.
There is a superclass
class NavigationView extends Backbone.View
config:
test:
string: "Test"
and two classes derived from it
class SubView extends NavigationView
initialize: ->
# Setting the view's template property using the Underscore template method
_.merge @config, {
test:
string: "bla"
}
class IndexView extends NavigationView
...
If I change within SubView's function initialize the config variable it is also changed in an instance of IndexView.
I instantiate my objects like so, within a BackBone.Router class:
index: () ->
# Instantiates a new view which will render the header text to the page
new IndexView()
sub: () ->
new SubView()
I created a fiddle to show it: http://jsfiddle.net/hijolan/9VeND/
Any ideas how to do that?
Best regards, hijolan
Upvotes: 2
Views: 680
Reputation: 434685
Your problem is that _.merge
modifies its first argument in-place:
_.merge(object [, source1, source2, …])
Merges enumerable properties of the source object(s) into the
destination
object.
Note that where the documentation says destination
it really means object
. The intent of merge
is to be a deep version of _.extend
and the Underscore docs are explicit about what happens:
extend
_.extend(destination, *sources)
Copy all of the properties in the source objects over to the destination object, and return the destination object.
You'll notice that lodash's extend
(AKA assign
) also gets the parameter names mixed up:
_.assign(object [, source1, source2, …])
Assigns own enumerable properties of
source
object(s) to thedestination
object.
Again they mean object
when they say destination
.
When you do this:
class NavigationView extends Backbone.View
config:
test:
string: "Test"
The config
ends up attached to the prototype for NavigationView
and so the exact same config
object will be seen by NavigationView
and its subclasses. That means that @config
is the prototype's config
in your initialize
:
_.merge @config, { test: { string1: "blub" } }
so _.merge
will merge the new values right into the prototype's config
and that makes the change visible all the way up to NavigationView
and down into all of its subclasses. If you trace the inheritance back up, you'll find that @config
in that context is from NavigationView
so your _.merge
is just a tricky and confusing way of writing:
_.merge NavigationView::config, ...
The root of the problem is that _.extend
and _.merge
modify their first argument. The way out of this trap is to supply a destination object that is safely writeable:
@config = _.merge { }, @config, { test: { string1: 'blub' } }
# ----------------^^^
Demo: http://jsfiddle.net/ambiguous/7j2FM/
Upvotes: 4