Object.create copying reference of array and object properties [duplicate] - javascript

This question already has answers here:
Crockford's Prototypal inheritance - Issues with nested objects
(3 answers)
Closed 7 years ago.
I am trying to understand how Object.create copies arrays and objects properties when initiating a new object. It seems to be different then copying a string or number. For example if we have a basic Object with a number and array property. jsfiddle example
var obj = {
num: 0, arr: []
};
We then initiate 3 new Objects from this base.
var set1 = Object.create(obj);
set1.num = 10;
set1.arr.push(1);
var set2 = Object.create(obj);
var set3 = Object.create(obj, {arr: []});
I was expecting set2.num and set2.arr property to be it's initial state. I found this to be true for the number, but not the array. Of course one work around is to pass {arr: []} when initiating the Object or creating a initiation function that resets the arr property.
// false
console.log(set1.num === set2.num);
// true - why is this true???
console.log(set1.arr === set2.arr);
// false
console.log(set1.arr === set3.arr);
Is this the normal behavior? Is Object.create keeping a reference to all of the Object's array and object properties? It would be very nice to not have to create new arrays and objects when initiating a new Object.

It would be very nice to not have to create new arrays and objects when initiating a new Object
Write a function in your favourite style
Returning a literal
function makeMyObject() {
return {num: 0, arr: []};
}
// usage
var obj = MyObject();
Returning an Object.created object, and assigning to it,
function makeMyObject() {
var o = Object.create(null); // or some prototype instead of `null`
return Object.assign(o, {num: 0, arr: []});
}
// usage
var obj = MyObject();
Using new
function MyObject() {
this.num = 0;
this.arr = [];
}
// usage
var obj = new MyObject();
Cloning is a bit more complicated, a basic example might be
function shallowClone(o) {
var e;
if (typeof o !== 'object')
return o;
e = Object.create(Object.getPrototypeOf(o));
// copy enumerable references
Object.assign(e, o);
// or to keep non-enumerable properties
// Object.defineProperties(b, Object.getOwnPropertyNames(o).map(Object.getOwnPropertyDescriptor.bind(Object, o)));
return e;
}
Deep cloning requires looping over properties (e.g. for..in for enumerable only) and type checking instead of simply copying everything over. You usually end up needing to recurse on properties which are Objects themselves.
For known types, you can teach it to use the correct constructor too, e.g.
if (Array.isArray(o)) {
e = [];
o.forEach((v, i) => e[i] = recurse(v));
}
Where recurse would be the name of the clone function

Related

Manipulating object in array changes object outside of array?

I don't understand why this behavior is happening. Lets say I define an object and make an array of 3 of this object. If I modify the objects in the array, it affects all instances of the object? Could someone explain why this is? Also, how do I make an array with independent "Copies" of the object to get the desired behavior? Thanks!
example
testObject = {"value1":"a","value2":"b"};
objArray = [];
for(i=0; i < 3; i++){
var newobj = testObject; //make a new testObject
objArray.push(newobj); //push new object to array
}
delete objArray[0].value2 // Desired, delete value 2 ONLY from array object 0
objArray[2].value2 //Undefined? Why is value2 missing from object 2
testObject.value2 //Undefined? Why is value2 missing from original object?
As opposed to primitives (strings, numbers, booleans, symbols null, undefined), objects in javascript are passed by reference. Variables serve as placeholders/pointers to these objects. To create a copy of an object without the risk of mutation you'd use spread (barring compatibility):
const newObject = { ...testObject };
or traditionally, Object.assign(), passing an empty object literal to avoid mutability of the original testObject:
const newObject = Object.assign({}, testObject);
As far as deep cloning, MDN suggests using a combination of JSON.parse() and JSON.stringify(). So for example:
const testObject = { value: "a", other: { value2: b } };
const newObject = JSON.parse(JSON.stringify(testObject));
You are pushing same object's reference again and again in the loop.
for(i=0; i < 3; i++){
var newobj = testObject; //no new object,same object's reference again
objArray.push(newobj); //push new object to array
}
It should be
for(i=0; i < 3; i++){
var newobj = {"value1":"a","value2":"b"}; //make a new testObject
objArray.push(newobj); //push new object to array
}
When creating an Object in JavaScript, you are actually creating a reference to that object. You can store this in a variable and pass it around ... perhaps append it to an array. When you go to do operations on an object reference, it finds the original object that the reference points to and updates it. Thus when you use .push it's not creating a new object but simply pushing the reference to that object. If you update it in one spot it will update it in the other and any others where you have assigned that reference.
Copying an object into a new object is generally called cloning. There are a lot of different ways to clone objects in JavaScript with varying results.
You can use var newobj = { ...testObject } as the other answer suggests. This spread operator essentially copies the properties of testObject and creates a new object (declared with the outer { }). The reference to that new object is then assigned to newobj. You can think of it as doing this:
var newobj = {
value1: testObject.value1,
value2: testObject.value2,
};
However, you should keep in mind that this gives you only one level of cloning. That is to say if your object contains other objects then the reference to that object will be assigned as the property rather than a clone of that object. For example: let's say you had:
var testObject = { obj: { a: "b" } };
var newobj = { ...testObject };
delete testObject.obj.a;
console.log(newobj); // { obj: {} }
In order to solve this, you need to do what is called a deep clone which in JavaScript can be done by recursively cloning object properties that are also objects. There are a bunch of ways to do this including libraries like lodash or home-grown functions. One example on SO: What is the most efficient way to deep clone an object in JavaScript?
Finally, if testObject is supposed to be something like an object template or initial state from which other newobj are derived, it might make more sense to use a function:
function newObjFactory() {
return {
value1: "a",
value2: "b",
};
}
Then you can do var newobj = newObjFactory() and you'll get a new object each time since a new object is created by the function each time it's called and returned.

Prototype lost on slice on array-like construct

I have created an array like prototype:
function METracker() {}
METracker.prototype = Object.create(Array.prototype);
METracker.prototype.myMethod = function(aStd) {
return true;
};
now i create an instance:
var aInst = new METracker('a', 'b', 'c');
Now I want to clone it so I do:
var cloneInst = aInst.slice();
however cloneInst no longer has the method .myMethod is there a way to keep the prototype on the clone?
Thanks
If you're going to create your own array-alike, the trick is to extend an array instance, not the array prototype:
function MyAry() {
var self = [];
[].push.apply(self, arguments);
// some dark magic
var wrap = 'concat,fill,filter,map,slice';
wrap.split(',').forEach(function(m) {
var p = self[m];
self[m] = function() {
return MyAry.apply(null, p.apply(self, arguments));
}
});
// add your stuff here
self.myMethod = function() {
document.write('values=' + this.join() + '<br>');
};
return self;
}
a = new MyAry(11,44,33,22);
a.push(55);
a[10] = 99;
a.myMethod()
b = a.sort().slice(0, 4).reverse();
b.myMethod();
Basically, you create a new array (a normal array, not your object), wrap some Array methods so that they return your object instead of generic arrays, and add your custom methods to that instance. All other array methods and the index operation keep working on your object, because it's just an array.
I have created an array like prototype:
No, you haven't.
function METracker() {}
METracker.prototype = Object.create(Array.prototype);
METracker.prototype.myMethod = function(aStd) {
return true;
};
The METracker constructor does nothing at all, it will just return a new Object with Array.prototype on its [[Prototype]] chain.
var aInst = new METracker('a', 'b', 'c');
Just returns an instance of METracker, it has no data since the constructor doesn't do anything with the arguments passed. Assigning Array.prototype to the inheritance chain doesn't mean the Array constructor is invoked.
var cloneInst = aInst.slice();
Note that callling slice() on aInst just returns a new, empty array. aInst doesn't have a length property, so the algorithm for slice has nothing to iterate over. And even if aInst had properties, slice will only iterate over the numeric ones that exist with integer values from 0 to aInst.length - 1.
If you want to create a constructor that creates Array–like objects, consider something like:
function ArrayLike() {
// Emulate Array constructor
this.length = arguments.length;
Array.prototype.forEach.call(arguments, function(arg, i) {
this[i] = arg;
}, this);
}
ArrayLike.prototype = Object.create(Array.prototype);
var a = new ArrayLike(1,2,3);
document.write(a.length);
document.write('<br>' + a.join());
The above is just play code, there is a lot more to do. Fixing the length issue isn't easy, I'm not sure it can be done. Maybe there needs to be a private "fixLength" method, but methods like splice need to adjust the length and fix indexes as they go, so you'll have to write a constructor that emulates the Array constructor and many methods to do re–indexing and adjust length appropriately (push, pop, shift, unshift, etc.).

Prototype array param [duplicate]

This question already has answers here:
Javascript object members that are prototyped as arrays become shared by all class instances
(3 answers)
Closed 6 years ago.
I think that I did not understand the whole prototype flow, I have this problem:
function SomeO() {};
SomeO.prototype.arr = [];
SomeO.prototype.str = "";
var s1 = new SomeO();
var s2 = new SomeO();
s1.str+="1"
console.log(s2) // "" OK
s1.arr.push(1)
console.log(s2) // [1] WHY???
Why when I add an item to an array in one object it has the same array instance?
That's because objects are shared by reference by all the instances of your "SomeO" object, in this case your "arr" attribute, and things like strings or numbers are shared by value, so the modification of the string will not affect the values of other instances.
so in that case is normal to get that result.
function SomeO() {};
SomeO.prototype.arr = [];
SomeO.prototype.str = "chimichangas";
var s1 = new SomeO();
var s2 = new SomeO();
s1.str+="1"
console.log(s1.str); // "chimichangas1" OK because is by value
console.log(s2.str); // "chimichangas" OK because is by value
s1.arr.push(1);
console.log(s2.arr); // [1] WHY??? because is by reference
And if you don't want to share the array you should do something like.
function SomeO() {
this.arr = [];
};
SomeO.prototype.str = "";
var s1 = new SomeO();
var s2 = new SomeO();
s1.str+="1"
console.log(s1.str); // "1" OK
console.log(s2.str); // "" OK
s1.arr.push(1);
console.log(s1.arr); // [1] Ok
console.log(s2.arr); // [] Ok
Because both instances share the same [[Prototype]](the Object assigned to SomeO.prototype), the also share the same Array in SomeO.prototype.arr.
You can check it yourself:
s1.arr === s2.arr
To circumvent this, you could define the array (and all other objects you might need) in the constructor instead:
function SomeO() {
this.arr = [];
this.obj = {}; // define all arrays and objects in the constructor if they need to be separate
}
Because that's how you defined the array: you create one array object on the prototype which is shared between all instances. Typically you'll only want to put functions and constant values on the prototype. Every instance property needs to be created inside the constructor:
function SomeO() {
this.arr = [];
}
When you reference a property of an object (e.g. s1.arr), it first checks if the property exists on the object, if it does, it returns it, if it doesn't, it falls back to the object's prototype.
When you do s1.str += "1", which is equivalent to s1.str = s1.str + "1", you're setting the str property on the object itself, the prototype's str property doesn't change. s1.str will return the new string from s1, and s2.str will fall back to prototype.str.
s1's str s2's str prototype's str s1.str s2.str
before: - - "" "" (from proto) "" (from proto)
after: "1" "" "1" (from own) "" (from proto)
When you do s1.arr.push(1) you get s1.arr from the prototype, and you change its value. You never set the arr property on s1.
s1's arr s2's arr prototype's arr s1.arr s2.arr
before: - - [] [] (from proto) [] (from proto)
after: - - [1] [1] (from proto) [1] (from proto)

How to copy a Map into another Map? [duplicate]

This question already has answers here:
Shallow-clone a Map or Set
(6 answers)
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 2 years ago.
How do I clone/copy a Map in JavaScript?
I know how to clone an array, but how do I clone/copy a Map?
var myArray = new Array(1, 2, 3);
var copy = myArray.slice();
// now I can change myArray[0] = 5; & it wont affect copy array
// Can I just do the same for map?
var myMap = new ?? // in javascript is it called a map?
var myMap = {"1": 1, "2", 2};
var copy = myMap.slice();
With the introduction of Maps in JavaScript it's quite simple considering the constructor accepts an iterable:
var newMap = new Map(existingMap)
Documentation here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
A simple way (to do a shallow copy) is to copy each property of the source map to the target map:
var newMap = {};
for (var i in myMap)
newMap[i] = myMap[i];
NOTE: newMap[i] could very well be a reference to the same object as myMap[i]
Very simple to just use Object.assign()
let map = {'a': 1, 'b': 2}
let copy = Object.assign({}, map);
// Or
const copy = Object.assign({}, {'a': 1, 'b': 2});
JQuery has a method to extend an object (merging two objects), but this method can also be used to clone an object by providing an empty object.
// Shallow copy
var newObject = jQuery.extend({}, oldObject);
// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);
More information can be found in the jQuery documentation.
If you need to make a deep copy of a Map you can use the following:
new Map(JSON.parse(JSON.stringify(Array.from(source))));
Where source is the original Map object.
Note this may not be suitable for all use cases where the Map values are not serializable, for more details see: https://stackoverflow.com/a/122704/10583071
There is nothing built in.
Either use a well tested recursive property copier or if performance isn't an issue, serialise to JSON and parse again to a new object.
There is no built-in (edit: DEEP) clone/copy. You can write your own method to either shallow or deep copy:
function shallowCopy(obj) {
var result = {};
for (var i in obj) {
result[i] = obj[i];
}
return result;
}
function deepCopy(obj) {
var result = {};
for (var i in obj) {
// recursion here, though you'll need some non-trivial logic
// to avoid getting into an endless loop.
}
return result;
}
[EDIT] Shallow copy is built-in, using Object.assign:
let result = Object.assign({}, obj);
All objects in Javascript are dynamic, and can be assigned new properties. A "map" as you refer to it is actually just an empty object. An Array is also an object, with methods such as slice and properties like length.
I noticed that Map should require special treatment, thus with all suggestions in this thread, code will be:
function deepClone( obj ) {
if( !obj || true == obj ) //this also handles boolean as true and false
return obj;
var objType = typeof( obj );
if( "number" == objType || "string" == objType ) // add your immutables here
return obj;
var result = Array.isArray( obj ) ? [] : !obj.constructor ? {} : new obj.constructor();
if( obj instanceof Map )
for( var key of obj.keys() )
result.set( key, deepClone( obj.get( key ) ) );
for( var key in obj )
if( obj.hasOwnProperty( key ) )
result[key] = deepClone( obj[ key ] );
return result;
}

Nodejs: how to clone an object

If I clone an array, I use cloneArr = arr.slice()
I want to know how to clone an object in nodejs.
For utilities and classes where there is no need to squeeze every drop of performance, I often cheat and just use JSON to perform a deep copy:
function clone(a) {
return JSON.parse(JSON.stringify(a));
}
This isn't the only answer or the most elegant answer; all of the other answers should be considered for production bottlenecks. However, this is a quick and dirty solution, quite effective, and useful in most situations where I would clone a simple hash of properties.
Object.assign hasn't been mentioned in any of above answers.
let cloned = Object.assign({}, source);
If you're on ES6 you can use the spread operator:
let cloned = { ... source };
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
There are some Node modules out there if don't want to "roll your own". This one looks good: https://www.npmjs.com/package/clone
Looks like it handles all kinds of stuff, including circular references. From the github page:
clone masters cloning objects, arrays, Date objects, and RegEx
objects. Everything is cloned recursively, so that you can clone dates
in arrays in objects, for example. [...] Circular references? Yep!
You can use lodash as well. It has a clone and cloneDeep methods.
var _= require('lodash');
var objects = [{ 'a': 1 }, { 'b': 2 }];
var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);
// => true
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
It's hard to do a generic but useful clone operation because what should be cloned recursively and what should be just copied depends on how the specific object is supposed to work.
Something that may be useful is
function clone(x)
{
if (x === null || x === undefined)
return x;
if (typeof x.clone === "function")
return x.clone();
if (x.constructor == Array)
{
var r = [];
for (var i=0,n=x.length; i<n; i++)
r.push(clone(x[i]));
return r;
}
return x;
}
In this code the logic is
in case of null or undefined just return the same (the special case is needed because it's an error to try to see if a clone method is present)
does the object have a clone method ? then use that
is the object an array ? then do a recursive cloning operation
otherwise just return the same value
This clone function should allow implementing custom clone methods easily... for example
function Point(x, y)
{
this.x = x;
this.y = y;
...
}
Point.prototype.clone = function()
{
return new Point(this.x, this.y);
};
function Polygon(points, style)
{
this.points = points;
this.style = style;
...
}
Polygon.prototype.clone = function()
{
return new Polygon(clone(this.points),
this.style);
};
When in the object you know that a correct cloning operation for a specific array is just a shallow copy then you can call values.slice() instead of clone(values).
For example in the above code I am explicitly requiring that a cloning of a polygon object will clone the points, but will share the same style object. If I want to clone the style object too instead then I can just pass clone(this.style).
There is no native method for cloning objects. Underscore implements _.clone which is a shallow clone.
_.clone = function(obj) {
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
It either slices it or extends it.
Here's _.extend
// extend the obj (first parameter)
_.extend = function(obj) {
// for each other parameter
each(slice.call(arguments, 1), function(source) {
// loop through all properties of the other objects
for (var prop in source) {
// if the property is not undefined then add it to the object.
if (source[prop] !== void 0) obj[prop] = source[prop];
}
});
// return the object (first parameter)
return obj;
};
Extend simply iterates through all the items and creates a new object with the items in it.
You can roll out your own naive implementation if you want
function clone(o) {
var ret = {};
Object.keys(o).forEach(function (val) {
ret[val] = o[val];
});
return ret;
}
There are good reasons to avoid deep cloning because closures cannot be cloned.
I've personally asked a question about deep cloning objects before and the conclusion I came to is that you just don't do it.
My recommendation is use underscore and it's _.clone method for shallow clones
For a shallow copy, I like to use the reduce pattern (usually in a module or such), like so:
var newObject = Object.keys(original).reduce(function (obj, item) {
obj[item] = original[item];
return obj;
},{});
Here's a jsperf for a couple of the options: http://jsperf.com/shallow-copying
Old question, but there's a more elegant answer than what's been suggested so far; use the built-in utils._extend:
var extend = require("util")._extend;
var varToCopy = { test: 12345, nested: { val: 6789 } };
var copiedObject = extend({}, varToCopy);
console.log(copiedObject);
// outputs:
// { test: 12345, nested: { val: 6789 } }
Note the use of the first parameter with an empty object {} - this tells extend that the copied object(s) need to be copied to a new object. If you use an existing object as the first parameter, then the second (and all subsequent) parameters will be deep-merge-copied over the first parameter variable.
Using the example variables above, you can also do this:
var anotherMergeVar = { foo: "bar" };
extend(copiedObject, { anotherParam: 'value' }, anotherMergeVar);
console.log(copiedObject);
// outputs:
// { test: 12345, nested: { val: 6789 }, anotherParam: 'value', foo: 'bar' }
Very handy utility, especially where I'm used to extend in AngularJS and jQuery.
Hope this helps someone else; object reference overwrites are a misery, and this solves it every time!
In Node.js 17.x was added the method structuredClone() to allow made a deep clone.
Documentation of reference: https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
I implemented a full deep copy. I believe its the best pick for a generic clone method, but it does not handle cyclical references.
Usage example:
parent = {'prop_chain':3}
obj = Object.create(parent)
obj.a=0; obj.b=1; obj.c=2;
obj2 = copy(obj)
console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3
console.log(obj2, obj2.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3
parent.prop_chain=4
obj2.a = 15
console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 4
console.log(obj2, obj2.prop_chain)
// '{'a':15, 'b':1, 'c':2} 4
The code itself:
This code copies objects with their prototypes, it also copy functions (might be useful for someone).
function copy(obj) {
// (F.prototype will hold the object prototype chain)
function F() {}
var newObj;
if(typeof obj.clone === 'function')
return obj.clone()
// To copy something that is not an object, just return it:
if(typeof obj !== 'object' && typeof obj !== 'function' || obj == null)
return obj;
if(typeof obj === 'object') {
// Copy the prototype:
newObj = {}
var proto = Object.getPrototypeOf(obj)
Object.setPrototypeOf(newObj, proto)
} else {
// If the object is a function the function evaluate it:
var aux
newObj = eval('aux='+obj.toString())
// And copy the prototype:
newObj.prototype = obj.prototype
}
// Copy the object normal properties with a deep copy:
for(var i in obj) {
if(obj.hasOwnProperty(i)) {
if(typeof obj[i] !== 'object')
newObj[i] = obj[i]
else
newObj[i] = copy(obj[i])
}
}
return newObj;
}
With this copy I can't find any difference between the original and the copied one except if the original used closures on its construction, so i think its a good implementation.
I hope it helps
Depending on what you want to do with your cloned object you can utilize the prototypal inheritence mechanism of javascript and achieve a somewhat cloned object through:
var clonedObject = Object.create(originalObject);
Just remember that this isn't a full clone - for better or worse.
A good thing about that is that you actually haven't duplicated the object so the memory footprint will be low.
Some tricky things to remember though about this method is that iteration of properties defined in the prototype chain sometimes works a bit different and the fact that any changes to the original object will affect the cloned object as well unless that property has been set on itself also.
Objects and Arrays in JavaScript use call by reference, if you update copied value it might reflect on the original object.
To prevent this you can deep clone the object, to prevent the reference to be passed, using lodash library cloneDeep method
run command
npm install lodash
const ld = require('lodash')
const objectToCopy = {name: "john", age: 24}
const clonedObject = ld.cloneDeep(objectToCopy)
Try this module for complex structures, developed especially for nodejs - https://github.com/themondays/deppcopy
Works faster than JSON stringify and parse on large structures and supports BigInt.
for array, one can use
var arr = [1,2,3];
var arr_2 = arr ;
print ( arr_2 );
arr=arr.slice(0);
print ( arr );
arr[1]=9999;
print ( arr_2 );
How about this method
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};

Categories

Resources