Duplicate object in javascript - javascript

I see two ways to duplicate objects
1.
var a={c:1}
var b=a;
alert(b.c);//alert 1
2.
var a={c:2};
var b={};
for (i in a)
{b[i]=a[i];}
alert(b.c);//alert 1
The first are shorter than the second so what is the efficiency in the second example?

In the first version you don't duplicate/clone the object you simply make a extra reference to it:
var a = { a: 1 };
var b = a;
b.a = 2;
console.log(a.a); // 2;
To clone an object there is numbers of libraries that can do that for you:
var b = $.extend({}, a); // Make a shallow clone (jQuery)
var b _.extend({}, a); // Make a shallow clone (underscore.js)
var b = $.extend(true, {}, a); // Make a deep clone (jQuery);
Or you can do it natively:
Simple clone:
var b = {};
var prop;
for (prop in a) {
b[prop] = a[prop];
}
Scratch of a deep clone function:
function deepClone(obj) {
var r;
var i = 0,
var len = obj.length;
// string, number, boolean
if (typeof obj !== "object") {
r = obj;
}
// Simple check for array
else if ( len ) {
r = [];
for ( ; i < len; i++ ) {
r.push( deepClone(obj[i]) );
}
}
// Simple check for date
else if ( obj.getTime ) {
r = new Date( +obj );
}
// Simple check for DOM node
else if ( obj.nodeName ) {
r = obj;
}
// Object
else {
r = {};
for (i in obj) {
r[i] = deepClone(obj[i]);
}
}
return r;
}

The first does not create a copy, but just copies the reference, so a and b point towards the same object after the operation.
In the second case, however, each attribute is copied separately, thus creating a "real" copy of the object in a (as long as there are just primitive types in the properties, else you got the same problem at a deeper level).
So in the first case if you change b.c then a.c will also change, while in the second case it wont.

As others have stated here: the first assignment, assigns a new reference to an existing object, the second performs a shallow copy.By shallow I mean: only the base object will be copied, there is no recursion:
var a = {some:'propery',
another:{might:'be',
an: 'object, too'}
};
var b = {};
for(var p in a)
{
b[p] = a[p];
}
b.some = 'b\'s own property';
console.log(a.some);//property -> unaltered
console.log(b.some);//b's own property --> separate entities
b.another.might = 'foo';
console.log(a.another.might);//foo ==> b.another references a.another
To solve this issue, you would be forgiven to think that a simple recursive function would suffice:
var cloneObj = function(o)
{
var p,r = {};
for (p in o)
{//omitting checks for functions, date objects and the like
r[p] = (o[p] instanceof Object ? cloneObj(o[p]) : o[p]);
}
};
But be weary of circular references!
//assume a is the same object as above
a._myself = a;//<- a references itself
This will produce endless recursion, aka a deadlock scenario, unless you add a check for just such cases:
var cloneObj = function(o)
{
var p,r = {};
for (p in o)
{//Needs a lot more work, just a basic example of a recursive copy function
switch(true)
{
case o[p] instanceof Function:
r[p] = o[p];
break;
case o[p] instanceof Date:
r[p] = new Date(o[p]);
break;
case o === o[p]:
//simple circular references only
//a.some.child.object.references = a; will still cause trouble
r[p] = r;
break;
case o[p] instanceof Array:
r[p] = o[p].slice(0);//copy arrays
break;
default:
r[p] = o[p] instanceof Object ? cloneObj(o[p]) : o[p];
}
}
return r;
};
Now, this is quite verbose, and in most cases utter overkill, if all you want are two objects with the same data, but can be altered independently (ie don't reference the same object in memory), all you need is 1 line of code:
var a = {some:'propery',
another:{might:'be',
an: 'object, too'}
};
var b = JSON.parse(JSON.stringify(a));
Bottom line: assigning a reference is certainly more efficient: it doesn't require the object constructor to be called a second time, nor does it require an additional copy of any of the constants. The downside is: you end up with a single object and might inadvertently change/delete something that you assume is still there when you're using the other reference (delete b.some;/*some time later*/a.some.replace(/p/g,'q');//<--error)

The first copies the reference and does not duplicate the object, the second creates a new reference and then copies the members (which, in turn, if they are references will just copy the references only).
You might want to look at this other SO - Does Javascript equal sign reference objects or clones them?
It's not a question of efficiency it's ultimately about correctness. If you need to share a reference to an object between different code blocks (e.g. so that multiple pieces of code can share the same object) - then you simply rely on the fact that javascript passes by reference.
If, however, you need to copy an object between methods - then you might be able to use your simple example in your second code block (if your objects don't have other 'objects' in them), otherwise you might have to implement a deep-clone (see How to Deep clone in javascript, and pay attention to the answer(s) there - it's not a trivial business).

Related

Why doesn't JS provide a simple way to perform a deep copy on objects?

As someone used to python and C++, having an = copy objects by reference rather than value is not intuitive at all. Not just that, but there seems to be no direct way of copying objects to begin with. JSON.parse(JSON.stringify) is the closest option (if I know correctly), and even that has problems.
a) In a language where all variables are anyway treated as objects, why does the = operator distinguish between primitive and non-primitive data types to decide whether to copy by value or reference?
b) Why is copy by value not possible for objects?
c) What techniques are helpful for a beginner used to copying objects by value to code without it?
a) In a language where all variables are anyway treated as objects,
why does the = operator distinguish [...] ?
The =(assign) operator does not distinguish between primitive and non primitive data types. It kinda does the same for both, considering that equality is preserved after assignment (excluding exceptions e.g. NaN, ...).
b) Why is copy by value not possible for objects?
Wrong assumption in a) leads to this. Assignment is no copy and a copy of an object does not preserve equality.
Or think about:
var obj = {a: {b: 1}}
.
What is the value of obj.a ? It is just the reference to {b:1}.
c) What techniques are helpful for a beginner used to copying objects
by value to code without it?
There are many approaches for this. And two trivial cases.
As the first case one knows the layout of the object. Thus creates a template or constructor and passes all values into the corresponding properties.
As the second case one assumes a cyclic object containing everything possible in javascript (functions, regexp, symbols, undefined, ...) of depth n and builds something (not json.stringify).
For starters: possible duplicate
Assumptions:
primitive and non primitive data types have default getter,setter, ...
I guess it's because of the specific nature of JS. When you create an object like this:
let obj = {a: 3, b: 5}
And try to pass this object to another variable like this:
let obj2 = obj
You will still have only 1 object, but with 2 references, so if you try to modify obj.a it will also affect obj2.a.
That's why I created a way around for myself, which may not be the best, but here is it:
//A little helper
Object.isObject = function (object) {
return object !== null && object instanceof Object && !Array.isArray(object)
}
/*
* Array and Object copy work with each other, so every nested array are
* copied properly
*/
/*
* ARRAY
*/
Object.defineProperty(Array.prototype, 'copy', {
value: function (array){
if(!(Array.isArray(array))) throw new TypeError('passed value should be an instance of array')
if(array.length <= 0) {
console.warn('WARNING: Found nothing to copy')
return this
}
this.splice(0, this.length)
for(let i = 0; i < array.length; i++) {
if (Array.isArray(array[i])) this[i] = Array().copy(array[i])
else if (Object.isObject(array[i])) this[i] = Object().copy(array[i])
else this[i] = array[i]
}
return this
},
enumerable: false
})
/*
* OBJECT
*/
Object.defineProperty(Object.prototype, 'copy', {
value: function (object){
if(!object || !(Object.isObject(object))) return false
if(Object.entries(object) <= 0) {
console.warn('WARNING: Found nothing to copy')
return this
}
const props = Object.getOwnPropertyNames(this)
for (let i = 0; i < props.length; i++) delete this[props[i]]
const keys = Object.keys(object)
const values = Object.values(object)
for (let i = 0; i < keys.length; i++) {
if(Array.isArray(values[i])) this[keys[i]] = Array().copy(values[i])
else if(Object.isObject([values[i]])) this[keys[i]] = Object().copy(values[i])
else this[keys[i]] = values[i]
}
return this
},
enumerable: false
})
//create 2 arrays
let a = [3, 5, {a: 5}, [3, 1]]
let b = []
//copy array of a
b.copy(a)
//modify values
b[0] = 6
b[2].a = 1
b[3][0] = 'test'
console.log(a) //source
console.log(b)
As you can see in the example these 2 arrays (a and b) are completely different and have no relation to each other.
P.S. Sorry if I wrote something wrong, my english is not that good :O

Copy a variable's value into another

I have a variable which has a JSON object as its value. I directly assign this variable to some other variable so that they share the same value. This is how it works:
var a = $('#some_hidden_var').val(),
b = a;
This works and both have the same value. I use a mousemove event handler to update b through out my app. On a button click, I want to revert b to the original value, meaning the value stored in a.
$('#revert').on('click', function(e){
b = a;
});
After this if I use the same mousemove event handler, it updates both a and b, when earlier it was updating only b as expected.
I'm stumped over this issue! What is wrong here?
It's important to understand what the = operator in JavaScript does and does not do.
The = operator does not make a copy of the data.
The = operator creates a new reference to the same data.
After you run your original code:
var a = $('#some_hidden_var').val(),
b = a;
a and b are now two different names for the same object.
Any change you make to the contents of this object will be seen identically whether you reference it through the a variable or the b variable. They are the same object.
So, when you later try to "revert" b to the original a object with this code:
b = a;
The code actually does nothing at all, because a and b are the exact same thing. The code is the same as if you'd written:
b = b;
which obviously won't do anything.
Why does your new code work?
b = { key1: a.key1, key2: a.key2 };
Here you are creating a brand new object with the {...} object literal. This new object is not the same as your old object. So you are now setting b as a reference to this new object, which does what you want.
To handle any arbitrary object, you can use an object cloning function such as the one listed in Armand's answer, or since you're using jQuery just use the $.extend() function. This function will make either a shallow copy or a deep copy of an object. (Don't confuse this with the $().clone() method which is for copying DOM elements, not objects.)
For a shallow copy:
b = $.extend( {}, a );
Or a deep copy:
b = $.extend( true, {}, a );
What's the difference between a shallow copy and a deep copy? A shallow copy is similar to your code that creates a new object with an object literal. It creates a new top-level object containing references to the same properties as the original object.
If your object contains only primitive types like numbers and strings, a deep copy and shallow copy will do exactly the same thing. But if your object contains other objects or arrays nested inside it, then a shallow copy doesn't copy those nested objects, it merely creates references to them. So you could have the same problem with nested objects that you had with your top-level object. For example, given this object:
var obj = {
w: 123,
x: {
y: 456,
z: 789
}
};
If you do a shallow copy of that object, then the x property of your new object is the same x object from the original:
var copy = $.extend( {}, obj );
copy.w = 321;
copy.x.y = 654;
Now your objects will look like this:
// copy looks as expected
var copy = {
w: 321,
x: {
y: 654,
z: 789
}
};
// But changing copy.x.y also changed obj.x.y!
var obj = {
w: 123, // changing copy.w didn't affect obj.w
x: {
y: 654, // changing copy.x.y also changed obj.x.y
z: 789
}
};
You can avoid this with a deep copy. The deep copy recurses into every nested object and array (and Date in Armand's code) to make copies of those objects in the same way it made a copy of the top-level object. So changing copy.x.y wouldn't affect obj.x.y.
Short answer: If in doubt, you probably want a deep copy.
I found using JSON works but watch our for circular references
var newInstance = JSON.parse(JSON.stringify(firstInstance));
newVariable = originalVariable.valueOf();
for objects you can use,
b = Object.assign({},a);
the question is already solved since quite a long time, but for future reference a possible solution is
b = a.slice(0);
Be careful, this works correctly only if a is a non-nested array of numbers and strings
The reason for this is simple. JavaScript uses refereces, so when you assign b = a you are assigning a reference to b thus when updating a you are also updating b
I found this on stackoverflow and will help prevent things like this in the future by just calling this method if you want to do a deep copy of an object.
function clone(obj) {
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
I do not understand why the answers are so complex. In Javascript, primitives (strings, numbers, etc) are passed by value, and copied. Objects, including arrays, are passed by reference. In any case, assignment of a new value or object reference to 'a' will not change 'b'. But changing the contents of 'a' will change the contents of 'b'.
var a = 'a'; var b = a; a = 'c'; // b === 'a'
var a = {a:'a'}; var b = a; a = {c:'c'}; // b === {a:'a'} and a = {c:'c'}
var a = {a:'a'}; var b = a; a.a = 'c'; // b.a === 'c' and a.a === 'c'
Paste any of the above lines (one at a time) into node or any browser javascript console. Then type any variable and the console will show it's value.
For strings or input values you could simply use this:
var a = $('#some_hidden_var').val(),
b = a.substr(0);
Most of the answers here are using built-in methods or using libraries/frameworks. This simple method should work fine:
function copy(x) {
return JSON.parse( JSON.stringify(x) );
}
// Usage
var a = 'some';
var b = copy(a);
a += 'thing';
console.log(b); // "some"
var c = { x: 1 };
var d = copy(c);
c.x = 2;
console.log(d); // { x: 1 }
I solved it myself for the time being. The original value has only 2 sub-properties. I reformed a new object with the properties from a and then assigned it to b. Now my event handler updates only b, and my original a stays as it is.
var a = { key1: 'value1', key2: 'value2' },
b = a;
$('#revert').on('click', function(e){
//FAIL!
b = a;
//WIN
b = { key1: a.key1, key2: a.key2 };
});
This works fine. I have not changed a single line anywhere in my code except for the above, and it works just how I wanted it to. So, trust me, nothing else was updating a.
A solution for AngularJS:
$scope.targetObject = angular.copy($scope.sourceObject)

Sort Array B after Array A, such that references and equal Primitives, maintain the exact Position

Update 2
I added a weight lookup to the sort function, which increased the performance by about 100% as well as the stability, as the previous sort function didn't consider all types, and as 1 == "1" the result depends on the initial order of the Array, as #Esailija points out.
The intent of the question is to improve this Answer of mine, I liked the question and since it got accepted and I felt like there is some performance to squeeze out of the sort function. I asked this question here since I hadn't many clues left where to start.
Maybe this makes things clearer as well
Update
I rephrased the complete question, as many people stated I was not specific enough, I did my best to specify what I mean. Also, I rewrote the sort function to better express the intent of the question.
Let arrayPrev be an Array (A) ,where A consists of 0 to n Elements' (E)
Let an Element either be
of a Primitive type
boolean, string, number, undefined, null
a Reference to an Object O, where O.type = [object Object] and O can consist of
0 to n Properties P, where P is defined like Element plus
an circular Reference to any P in O
Where any O can be contained 1 to n times. In the sense of GetReferencedName(E1) === GetReferencedName(E2)...
a Reference to an O, where O.type = [object Array] and O is defined like A
a circular Reference to any E in A
Let arrayCurr be an Array of the same length as arrayPrev
Illustrated in the following example
var x = {
a:"A Property",
x:"24th Property",
obj: {
a: "A Property"
},
prim : 1,
}
x.obj.circ = x;
var y = {
a:"A Property",
x:"24th Property",
obj: {
a: "A Property"
},
prim : 1,
}
y.obj.circ = y;
var z = {};
var a = [x,x,x,null,undefined,1,y,"2",x,z]
var b = [z,x,x,y,undefined,1,null,"2",x,x]
console.log (sort(a),sort(b,a))
The Question is, how can I efficiently sort an Array B, such that any Reference to an Object or value of a Primitive, shares the exact same Position as in a Previously, by the same compareFunction, sorted, Array A.
Like the above example
Where the resulting array shall fall under the rules.
Let arrayPrev contain the Elements' of a and arrayCurr contain the Elements' of b
Let arrayPrev be sorted by a CompareFunction C.
Let arrayCurr be sorted by the same C.
Let the result of sorting arrayCur be such, that when accessing an E in arrayCur at Position n, let n e.g be 5
if type of E is Object GetReferencedName(arrayCurr[n]) === GetReferencedName(arrayPrev[n])
if type of E is Primitive GetValue(arrayCurr[n]) === GetValue(arrayPrev[n])
i.e b[n] === a[n] e.g b[5] === a[5]
Meaning all Elements should be grouped by type, and in this sorted by value.
Where any call to a Function F in C shall be at least implemented before ES5, such that compatibility is given without the need of any shim.
My current approach is to Mark the Objects in arrayPrev to sort them accordingly in arrayCurr and later delete the Marker again. But that seems rather not that efficient.
Heres the current sort function used.
function sort3 (curr,prev) {
var weight = {
"[object Undefined]":6,
"[object Object]":5,
"[object Null]":4,
"[object String]":3,
"[object Number]":2,
"[object Boolean]":1
}
if (prev) { //mark the objects
for (var i = prev.length,j,t;i>0;i--) {
t = typeof (j = prev[i]);
if (j != null && t === "object") {
j._pos = i;
} else if (t !== "object" && t != "undefined" ) break;
}
}
curr.sort (sorter);
if (prev) {
for (var k = prev.length,l,t;k>0;k--) {
t = typeof (l = prev[k]);
if (t === "object" && l != null) {
delete l._pos;
} else if (t !== "object" && t != "undefined" ) break;
}
}
return curr;
function sorter (a,b) {
var tStr = Object.prototype.toString
var types = [tStr.call(a),tStr.call(b)]
var ret = [0,0];
if (types[0] === types[1] && types[0] === "[object Object]") {
if (prev) return a._pos - b._pos
else {
return a === b ? 0 : 1;
}
} else if (types [0] !== types [1]){
return weight[types[0]] - weight[types[1]]
}
return a>b?1:a<b?-1:0;
}
}
Heres a Fiddle as well as a JSPerf (feel free to add your snippet)
And the old Fiddle
If you know the arrays contain the same elements (with the same number of repetitions, possibly in a different order), then you can just copy the old array into the new one, like so:
function strangeSort(curr, prev) {
curr.length = 0; // delete the contents of curr
curr.push.apply(curr, prev); // append the contents of prev to curr
}
If you don't know the arrays contain the same elements, it doesn't make sense to do what you're asking.
Judging by the thing you linked, it's likely that you're trying to determine whether the arrays contain the same elements. In that case, the question you're asking isn't the question you mean to ask, and a sort-based approach may not be what you want at all. Instead, I recommend a count-based algorithm.
Compare the lengths of the arrays. If they're different, the arrays do not contain the same elements; return false. If the lengths are equal, continue.
Iterate through the first array and associate each element with a count of how many times you've seen it. Now that ES6 Maps exist, a Map is probably the best way to track the counts. If you don't use a Map, it may be necessary or convenient to maintain counts for items of different data types differently. (If you maintain counts for objects by giving them a new property, delete the new properties before you return.)
Iterate through the second array. For each element,
If no count is recorded for the element, return false.
If the count for the element is positive, decrease it by 1.
If the count for the element is 0, the element appears more times in the second array than in the first. Return false.
Return true.
If step 4 is reached, every item appears at least as many times in the first array as it does in the second. Otherwise, it would have been detected in step 3.1 or 3.3. If any item appeared more times in the first array than in the second, the first array would be bigger, and the algorithm would have returned in step 1. Thus, the arrays must contain the same elements with the same number of repetitions.
from the description, it appears you can simply use a sort function:
function sortOb(a,b){a=JSON.stringify(a);b=JSON.stringify(b); return a===b?0:(a>b?1:-1);}
var x = {a:1};
var y = {a:2};
var a = [1,x,2,3,y,4,x]
var b = [1,y,3,4,x,x,2]
a.sort(sortOb);
b.sort(sortOb);
console.log (a,b);
Your function always returns a copy of sort.el, but you can have that easier:
var sort = (function () {
var tmp;
function sorter(a, b) {
var types = [typeof a, typeof b];
if (types[0] === "object" && types[1] === "object") {
return tmp.indexOf(a) - tmp.indexOf(b); // sort by position in original
}
if (types[0] == "object") {
return 1;
}
if (types[1] == "object") {
return -1;
}
return a > b ? 1 : a < b ? -1 : 0;
}
return function (el) {
if (tmp) {
for (var i = 0; i < el.length; i++) {
el[i] = tmp[i]; // edit el in-place, same order as tmp
}
return el;
}
tmp = [].slice.call(el); // copy original
return tmp = el.sort(sorter); // cache result
};
})();
Note that I replaced sort.el by tmp and a closure. Fiddle: http://jsfiddle.net/VLSKK/
Edit: This (as well as your original solution)
only works when the arrays contain the same elements.
maintains the order of different objects in a
but
does not mess up when called more than twice
Try this
function ComplexSort (a, b)
{
if(typeof a == "object") {
a = a.a;
}
if(typeof b == "object") {
b = b.a;
}
return a-b;
}
var x = {a:1};
var y = {a:2};
var a = [1,x,2,3,y,4,x]
var b = [1,y,3,4,x,x,2]
a.sort(ComplexSort);
b.sort(ComplexSort);
console.log ("Consistent", a,b);

Creating new variable from another

What would be the best way to create a new array or object from another. Since doing
var oldvar = {x:1,y:2} //or [x,y]
var newvar = oldvar
will link them, what would be the bets best way to clone or cope the new variable?
Numbers in JavaScript
Numbers in JavaScript are what the spec calls 'Primitive Value Type'
From the specification about Numbers:
Number value # Ⓣ
primitive value corresponding to a double-precision 64-bit binary format IEEE 754 value.
NOTE A Number value is a member of the Number type and is a direct representation of a number.
So in your case newvar will be a copy of oldvar and not a reference.
In JavaScript, Number, Boolean, undefined, null or String are value types.
When passing any of these 5 around, you are in fact passing values and not references, no need to clone those.
When you pass anything else around (Objects) need to use cloning since they are reference types.
When cloning objects in JavaScript there are two approaches.
Shallow Cloning
This means you clone 1 level deep. Assuming all our properties in the object are enumerable (This is usually the case if you haven't used property descriptors) you can use something like:
var a = {a:3,b:5};
var copy = {};
for(var prop in a){
copy[prop] = a[prop];
}
However, often we want to copy the object's own properties and not everything it might inherit from its prototype, so we can do:
var copy = {};
for(var prop in a){
if(a.hasOwnProperty(prop)){//check that the cloned property belongs to _a_ itself
copy[prop] = a[prop];
}
}
Note these two only shallow copy properties off a, they do not deal with setting the prototype, and clone all the properties by reference (Except properties who are primitive value types themselves :) ).
Deep copying
Deep copying means creating a clone of the object that is several levels deep. This calls to recursion since deep copying is defined as such (in psuedocode)
CopyObject(object)
If object is a primitive value type
return object
clone := Empty Object
For every member of object
Add CopyObject(member) as a property to clone
Return clone
We are applying the algorithm recursively on object properties of the clone.
Here is a sample implementation that I documented for you. It assumes ES5 (Chrome) but you can easily adapt it to other/older browsers. It does more stuff like treat Date and Regex like special cases. It also keeps a dictionary of properties it already handled so it will be able to handle circular references in an object. It is intended for learning and not for production use :) If you have any questions about it feel free.
var clone = function (a) {
var passedRefs = []; // Keep track of references you passed to avoid cycles
var passedRefCreated = [];
function clone2(a1) { // Inner function to handle the actual cloning
var obj;
if (typeof a1 !== "object" || a1 === null) { // Handle value type
return a1;
}
var locInpPassed = passedRefs.indexOf(a1); // Detect circular reference
if (locInpPassed !== -1) {
return passedRefCreated[locInpPassed];
}
passedRefs.push(a1); // Add the object to the references to avoid circular references later
if (a1 instanceof Date) { // Handle date and RegExp for special cases
obj = new Date(a1.getTime());
} else if (a1 instanceof RegExp) {
obj = new RegExp(a1);
}else if (Array.isArray(a1)){// handle arrays in order for Array.isArray to work. Thanks FizzyTea for catching this.
obj = [];
} else { // Create a new object with the prototype of the one we're cloning to support prototypical inheritance. Prototypes are _shared_
obj = Object.create(Object.getPrototypeOf(a1));
}
passedRefCreated[passedRefs.indexOf(a1)] = obj; // Add to the references created dict
Object.getOwnPropertyNames(a1).forEach(function (prop) { // Go through all the property, even the ones that are not enumerable
obj[prop] = clone2(a1[prop]); // Call the algorithm recursively, just like in the pseudo code above
});
return obj;
}
return clone2(a); // Call the inner function that has access to the dictionary
}
(For example, you can use a for... in loop to iterate through the properties).
I wrote 2 relation functions for deep copying arrays and objects in javascript:
function clone_object(o) {
var r = {};
for (var p in o) {
if (o[p].constructor == Array) {
r[p] = clone_array(o[p]);
} else if (o[p].constructor == Object) {
r[p] = arguments.callee(o[p]);
} else {
r[p] = o[p];
}
}
return r;
}
function clone_array(o) {
var r = [];
for (var p = 0, l = o.length; p < l; p++) {
if (o[p].constructor == Array) {
r[p] = arguments.callee(o[p]);
} else if (o[p].constructor == Object) {
r[p] = clone_object(o[p]);
} else {
r[p] = o[p];
}
}
return r;
}
Example:
var o = { name: 'Prototype', version: 1.5, authors: ['sam', 'contributors'] };
var o2 = clone_object(o);
o2.authors.pop();
alert(o.authors);
// -> ['sam', 'contributors']
alert(o2.authors);
// -> ['sam']

Javascript: make an deep clone/extend/copy function that does not make any garbage for GC

I would like to make an extendDeep() function that does not make any garbage for GC.
The garbage collector need to be as inactive as possible.
ref.: https://www.scirra.com/blog/76/how-to-write-low-garbage-real-time-javascript
This is the extendDeep() function I want to modify:
function extendDeep(parent, child) {
var i, toStr = Object.prototype.toString,
astr = "[object Array]";
child = child || {};
for (i in parent) {
if (parent.hasOwnProperty(i)) {
if (typeof parent[i] === 'object') {
child[i] = (toStr.call(parent[i]) === astr) ? [] : {};
extendDeep(parent[i], child[i]);
} else {
child[i] = parent[i];
}
}
}
return child;
}
The function does not have to return anything. since the retuned object is why the garbage is made.
It is assumed that all the propertis of the parent object is available by reference (reuse of objects)
A JS interpreter might avoid creating a string when doing toStr.call(parent[i]) but if you can't rely on them doing that optimization then you can avoid creating strings in the very common case by changing
toStr.call(parent[i]) === astr
to
parent[i] instanceof Array // Is a regular array.
|| (!(parent[i] instanceof Object) // Is cross-frame
&& 'number' === typeof parent[i].length // Might be an array
&& toStr.call(parent[i]) === astr) // So check the hidden [[Class]] property.
If you know you're dealing with objects created by constructors from the same frame (so no cross-frame object sharing) then you can get by with just
parent[i] instanceof Array
This is actually a more interesting question than I first thought. After reading the suggested link it is clear the articles author is advocating object pooling. So something like
function Pool(fConstructor, nMaxSize, fCleanFunction) {
this.aObjectPool = [];
this.nMaxSize = nMaxSize;
this.fCleanFunction = fCleanFunction;
this.fConstructor = fConstructor;
}
Pool.prototype.get = function() {
return this.aObjectPool.pop() || new this.fConstructor();
}
Pool.prototype.recycle = function(oObject) {
if (aObjectPool.length < this.nMaxSize) {
fCleanFunction(oObject);
this.aObjectPool.push(oObject);
}
}
function wipeArray(aArray) {
aArray.length = 0;
}
function wipeObject(oObject) {
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
delete obj[p];
}
}
};
var oArrayPool = new Pool(Array, 50, wipeArray);
var oObjectPool = new Pool(Object, 50, wipeObject);
could be used to implement the pool. Then you'd replace the []'s and {}'s in the extend deep function with pool.get()'s.
Of course, for this to work you'd also need to ensure you were recycling old objects and arrays rather than just leaving them for garbage collection.
The 1st thing you need to decide is whether you want a clone or a copy, they are 2 different things.
The code you have given does a copy (and not a great one, because the use of hasOwnProperty means you could easily end up with non functional copies). A clone would be something like.
function Clone(){}
function clone(object) {
Clone.prototype = object;
return new Clone();
}
var objectToClone = {};
var clonedObject = clone(objectToClone);
The difference being that for a copy, changes to the original object will not affect the copy. For a clone, changes to the original object will affect the clone unless the clone has overwritten the property.

Categories