Deep Cloning Backbone.js Models - javascript

I am working in jquery with backbone.js and running into the case where i need to duplicate models, but i need to do a deep copy on them so no references exist between the copies. Models can have other models as attributes. Models can have anon functions as attributes.
So i'm working on creating an algorithm that will deep clone most backbone models. I’m expecting that all bindings should be removed (for the new instance) during this copy so I’m not worried about trying to keep them.
Goals:
Able to duplicate all simple variable (String, Int, float, etc) and store it into the new model, as the same name.
Done, using toJSON to create a new JSON object which can be passed to set(). This object only contains simple attributes, i.e. does not include attributes assigned to functions or other models.
Able to duplicate the anon functions some variables will be assigned to, without knowing function/attribute names beforehand.
If I know the name of the attribute which is assigned to a function I can copy it. But if the model is new or unknown I don’t have that information.
If an attribute is another backbone model, call the deep copy algorithm recursively on that attribute.
Not able to check if an attribute is a backbone model with native backbone methods, looking for a work around.
The simplified version of what I currently have is below:
/**
* Performs a deep copy of a backbone.js model
* All bindings for the copy are lost
* #param orgModel - the original model to copy
*/
function deepCopyModel(orgModel)
{
var dupModel = Backbone.Model.extend({});
var orgAttributes= orgModel.toJSON();
var keepAttr=_.keys(orgAttributes);
//remove any special cases
keepAttr=_.without( keepAttr , 'specialCase1', 'specialCase2' );
//or keepAttr=_.difference(keepAttr, ['specialCase1', 'specialCase2'] );
//remove undefined values
keepAttr=_.filter(keepAttr,function(key) {
return ( typeof(attributes[key])!="undefined" );
});
//grab the resulting list of attributes after filtering
var result=_.pick(attributes,keepAttr);
//assign attributes to the copy using set
dupModel.set(result);
//TODO: Implement deep copy of functions
//TODO: Implement deep copy of inner models
return dupModel;
}
Any help or insight you can give would be greatly appreciated. Thanks!

jQuery's extend method allows you to simply copy object properties from one to another.
Here's a contrived, but illustrative example. It even shows why you would not need to "deep" copy functions!
var someObj = {
a : "a",
b : 12345,
c : {
d : "d",
e : "e"
},
f : function() {
alert(this.a);
}
};
//copy from original to new empty object
var deepCopy = $.extend(true, {}, someObj);
deepCopy.a = "deepCopy.a";
deepCopy.c.d = "deepCopy.c.d";
alert("someObj is not affected when deep copying: " + someObj.c.d);
alert("deepCopy is entirely distinct when deep copying: " + deepCopy.c.d);
deepCopy.f();
someObj.f();
Here's a fiddle for your convenience: http://jsfiddle.net/S6p3F/3/
Running this code you will see that someObj and deepCopy are identical in structure but distinct objects.
As you can see, deep copying of functions is not required as the this reference is bound to whatever object the function is applied to. This is becausein javascript, calling a function as deepCopy.f() is functionally equivalent to deepCopy.f.call(deepCopy). A more illustrative example:
function someFunction() {
alert(this.someProperty);
}
var a = {
someProperty: "a's property"
},
b = {
someProperty: "b's property"
};
someFunction.call(a);
someFunction.call(b);
And a fiddle: http://jsfiddle.net/S6p3F/2/

If you are using Lo-Dash as Underscore drop-in replacement, you can also use _.cloneDeep
var newModel = new MyModel(_.cloneDeep(oldModel.toJSON());

Related

How to track changes with shared, immutable reference types in Javascript

Consider the following example:
function add(x, y) { return x + y; }
var collection = Object.freeze([1, 2, 3, 4]);
var consumerA = collection; // expects steady data
var consumerB = collection; // requires the latest data
var updatedCollection = collection.concat(5);
consumerA.reduce(add, 0); // 10 (desired result)
consumerB.reduce(add, 0); // 10 (incorrect result, should be 15)
consumerA operates with the immutable data it expects. What can be done in Javascript to ensure that consumerB always accesses the latest data?
Please notice: Just deep copying consumerA and treating collection as mutable data isn't an option.
UPDATE: The example merely serves to illustrate the fundamental problem, which is caused by shared reference types: Some consumers (or reference holder) rely on immutable, others on mutable data. I'm looking for a proper change tracking mechanism that solves this problem without undermining the benefits of immutable data.
Maybe the term "change tracking" is too vague. With change tracking I mean a way for consumerB to be informed about the change (push mechanism) or (more interesting) to be able to discover the change (pull mechanism). The latter would require that consumerB somehow gets access to the updated collection.
You use Object.freeze when you declare collection, so you can't added properties to collection.
When you create consumerB, you execute a copy of the object collection
var consumerB = collection;
So you can't added properties to consumerB like collection.
You need to clone the object instead copy it. You can do it like :
var consumerB = JSON.parse(JSON.stringify(collection));
Well, that's my only solution, but there are probably others. I wrap my immutable collection in a mutable object. A consumer which needs constant data holds a reference to the collection itself. A consumer which requires current state holds a reference to the wrapper. I use a primitive form of structural sharing in order to avoid cloning:
function add(x, y) { return x + y; }
var collection = Object.freeze([1, 2, 3, 4]);
var atom = {state: collection};
var consumerA = collection;
var consumerB = atom;
console.log(consumerA === consumerB.state); // true (obviously)
// naive structural sharing to avoid cloning
atom.state = Object.create(atom.state, {length: {value: atom.state.length, writable: true}});
atom.state.push(5);
Object.freeze(atom.state);
// as desired
console.log(consumerA.reduce(add, 0)); // 10
console.log(consumerB.state.reduce(add, 0)); // 15
// structural sharing is used
console.log(Object.getPrototypeOf(consumerB.state) === collection); // true
// object comparison simply by reference check
console.log(consumerA === consumerB.state); // false
By wrapping an immutable collection in a mutable wrapper it becomes a kind of persistent data type. That means it can be treated as a normal, mutable object but leaves its previous versions untouched, hence persistent. By the way, to name the wrapper atom isn't an accident, but a reference to the corresponding data type in Clojure.
Please note: To use the prototype system for structural sharing can lead to memory leaking and should be used with caution only.

Most efficient way to reference nested object

My Query:
What is the most efficient way to reference nested JavaScript objects? I have found a couple so far, and am wondering which is the most efficient, and if there are any others which are better than those which I list here. The problem involves usage of a root object, similarly to that which many JavaScript frameworks use. The root object is compulsory. Please do not post answers which do not use a root object.
Note that this is specifically intended for root/sub-root objects which have long names - referencing a.b would be useless, whilst referencing abcdefghijklm.nopqrstuvwxyz would be extremely useful.
EDIT:
Just to clarify a couple of things:
Via efficiency I mean firstly code length (which could in turn affect performance, but that is slightly less important). I am concerned about code length partially because of file size - yes, I would be minifying it afterwards - but also due to readability for anyone viewing my original code (including myself). Performance is also slightly important, I don't want long workarounds which would slow down execution.
The referencing would be mostly for commonly used sub-objects, NOT EVERY SINGLE ONE. I state (in method 1) that many global variables would need to be created - due to the fact that I was assuming that all of the first-level sub-objects would be commonly used.
Objects to be referenced:
The object foo contains the various sub-objects bar, baz and qux, each of which contain various other objects.
var foo = {
bar: {
bar: {},
baz: {},
qux: {}
},
baz: {
bar: {},
baz: {},
qux: {}
},
qux: {
bar: {},
baz: {},
qux: {}
}
};
Method 1 - Reference Variables:
The first method involves creating a variable for each of the sub-objects.
var bar = foo.bar;
var baz = foo.baz;
var qux = foo.quz;
foo.bar.baz could then be referenced simply via bar.baz, and foo.qux.qux via qux.qux.
Advantages:
Reference variables can be very concise - a single character is the minimum possible length.
Disadvantages:
If there are many sub-objects to be referenced, this would increase the global variable count. This is likely to cause conflicts, cross-script and otherwise - especially using single-character variable names.
A new reference variable needs to be created for every nested object. Not only is this inconvenient, but it also requires the programmer to remember to create each variable after amending the object. Conversely, if a sub-object is removed and the programmer forgets to remove the reference variable also, then the code becomes messy with useless global variables cluttering it up.
Method 2 - Reference Function:
The second method involves creating a function which returns a sub-object, depending on a couple of parameters.
function ref(a, b) {
//Ensure that the first parameter is passed:
if (!a) {
return false;
}
return foo.a.b;
};
foo.bar.baz could then be referenced via ref("bar", "baz"), and foo.qux,qux via ref("qux", "qux").
Advantages:
Works for all first-level sub-objects, without repetitive and messy separate variable defining.
Disadvantages:
Only really useful for shortening the root object - if the root object is named a then using the reference function would actually lengthen the code.
Let's say we have a graphic control object, that is an instance of a GraphicObject Class (function).
Let's say one instance looks like :
grControl = {
visual : {
boundingBox : { x : , y : , width: , height : ... },
...
},
action : {
...
}
};
The right way to create shortcuts is either :
• to cache locally the appropriate object in a var in a function that will use it :
function drawBBox (someGraphicControl) {
var bbox = someGraphicControl.visual.boundingBox;
// maybe you want also to cache sub-properties :
var width = bbox.width;
...
}
• to define, on the prototype, a getter and/or a setter that will provide an access to the nested property :
// read-only example
Object.defineProperty(GraphicControl.prototype, 'width',
{ get : function() {
return this.visual.boundingBox.width },
enumerable : true};
// read-write example
Object.defineProperty(GraphicControl.prototype, 'width',
{ get : function() {
return this.visual.boundingBox.width },
set : function(val) {
this.visual.boundingBox.width = val ; },
},
enumerable : true};
this way you can use :
grObject.width
and exactly refer to :
grObject.visual.boundingBox.width
The usual practice in javascript depends upon the circumstances:
1) One-time access to that object in a particular function. If you're just making a one time access to a deeply nested reference, you just spell out the reference with all the intervening names:
var data = foo.baz.quz.prop1;
2) Multiple-accesses to a particular nested object. If there are multiple references to a particular deeply nested object, then you create a temporary local variable that points to the common object and then reference from that within the local function.
function getDeepData() {
var obj = foo.baz.quz;
var prop1 = obj.prop1;
var prop2 = obj.prop2;
var prop2 = obj.prop3;
// code that uses prop1, prop2 and prop3 here
}
3) Iterating properties who's names are not known in advance. Since you don't know the property names in advance, this is done by iterating one level at a time (sometimes recursively) and just keeping the parent object in a local variable.
If you find yourself making lots of deeply nested one-time references all over your code, then you probably want to revisit the structure of your code or data because that can usually be avoided.

IE9 does not recognize prototype function?

I'm working on an AngularJS SPA and I'm using prototypes in order to add behavior to objects that are incoming through AJAX as JSON. Let's say I just got a timetable x from an AJAX call.
I've defined Timetable.prototype.SomeMethod = function() and I use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf in order to set the prototype of x to TimeTable.prototype. I have the polyfill in place too.
If I call x.SomeMethod() this works in IE > 9, FF, Chrome etc. However, IE 9 gives me a headache and says throws an error stating 'x does not have property or member SomeMethod'.
Debugging in IE shows me that the _proto_ of x has SomeMethod() in the list of functions, however, calling x.SomeMethod() gives the same error as described.
How can I make this work in IE9 ?
More comment than answer
The main problem with "extending" a random object retrieved from some other environment is that javascript doesn't really allow random property names, e.g. the random object may have a property name that shadows an inherited property. You might consider the following.
Use the random object purely as data and pass it to methods that access the data and do what you want, e.g.
function getName(obj) {
return obj.name;
}
So when calling methods you pass the object to a function that acts on the object and you are free to add and modify properties directly on the object.
Another is to create an instance with the methods you want and copy the object's properties to it, but then you still have the issue of not allowing random property names. But that can be mitigated by using names for inherited properties that are unlikely to clash, e.g. prefixed with _ or __ (which is a bit ugly), or use a naming convention like getSomething, setSomething, calcLength and so on.
So if obj represents data for a person, you might do:
// Setup
function Person(obj){
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
this[p] = obj[p];
}
}
}
Person.prototype.getName = function(){
return this.name;
};
// Object generated from JSON
var dataFred = {name:'fred'};
// Create a new Person based on data
var p = new Person(dataFred);
You might even use the data object to create instances from various consructors, e.g. a data object might represent multiple people, or a person and their address, which might create two related objects.
This is how I solved it at the end:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
if (!isIE9()) {
obj.__proto__ = proto;
} else {
/** IE9 fix - copy object methods from the protype to the new object **/
for (var prop in proto) {
obj[prop] = proto[prop];
}
}
return obj;
};
var isIE9 = function() {
return navigator.appVersion.indexOf("MSIE 9") > 0;
};

Set a prototype for a predefined object in javascript

How can I manipulate the prototype of a predefined object (for example an Array) so that it does something when creating instances of that object?
Simply I want to alert('an array was created!') whenever an Array is instantiated.
You can set a new method on an array by adding it to the Array.prototype object:
Array.prototype.fizz = function () {
alert('works');
};
var arr = [];
arr.fizz();
However, this only allows you to create new methods, this does not allow you to extend existing methods*, nor does it allow you to override the Array constructor.
Be careful adding new methods to existing types. This can adversely affect the entire scripting environment, and cause unexpected behavior in some libraries. Although some might call it "bad practice", it's quite common to use polyfills for cross-browser compatibility, such as by creating Array.prototype.indexOf.
There is no such thing as a "newed" array, the word is "instantiated":
var a1, a2;
a1 = []; //a1 was instantiated with a new Array
a2 = new Array(); //a2 was also instantiated with a new Array
There is no cross-browser means of overriding the Array constructor.
* it's possible to wrap an existing method in a function so that, when called, the existing method performs its original functionality, in addition to the new functionality. Although this might be referred to as extending an existing method, it is in fact creating a new method.
You can try to override Array with your own function. It seems to work when doing new Array, but not when doing [].
(function() {
var _ac = Array;
Array = function() {
alert('an array was newed!');
return _ac.apply(this, arguments);
};
}());
DEMO: http://jsfiddle.net/DAg9A/
I would suggest the you just create an array namespace for it:
array = {};
array.create = function() {
alert("Created an array");
return [];
}
So whenever you create an array you use: array.create(); .
You should not, and in this case can not, change native functionality. You have to be in charge of every array creation.

How to copy/clone a hash/object in JQuery?

I have a simple object (or hash) in Javascript:
var settings = {
link: 'http://example.com',
photo: 'http://photos.com/me.jpg'
};
I need a copy of it. Is there a settings.clone() type method that will give me another object with the same attributes? I'm using jQuery, so happy to use a jQuery utility method if one exists.
Yes, extend an empty object with the original one; that way, everything will simply be copied:
var clone = $.extend({}, settings);
Extending some filled object with another, e.g.:
$.extend({a:1}, {b:2})
will return:
{a:1, b:2}
With the same logic:
$.extend({}, {foo:'bar', test:123})
will return:
{foo:'bar', test:123}
i.e. effectively a clone.
In a non jQuery way.
var newObj = {};
Object.keys(settings).forEach(function(key) {
newObj[ key ] = settings[ key ];
});
This copies only the top-level properties. To copy hashes with nested objects as property values, you will need to use a recursive function.
NB: The Object.keys(settings) avoids the need for calling settings.hasOwnProperty(key).
var clone = $.extend(true, {}, settings);
Set first argument to true.
EDIT: First argument true signifies deep copy. For given example in original question there is no need for deep copy since there are simple immutable key-value pairs. For question in title - as a general case - use deep copy. Otherwise you get half-copy.
It sounds like you want jQuery extend, which can copy an object for you.
http://api.jquery.com/jQuery.extend/
My 2 cents:
function clone(hash) {
var json = JSON.stringify(hash);
var object = JSON.parse(json);
return object;
}
It may not be the most optimized option but it can be handy for some scenarios.
Underscore.js also has an extend function if you are not using jQuery:
extend _.extend(destination, *sources) Copy all of the properties in the source objects over to the destination object, and
return the destination object. It's in-order, so the last source will
override properties of the same name in previous arguments.
_.extend({name: 'moe'}, {age: 50});
=> {name: 'moe', age: 50}

Categories

Resources