I set out to find and understand a nice way to merge objects in Vanilla JS. My requirements for the function are very simple (borrowed from here):
Merge two objects x and y deeply, returning a new merged object with the elements from both x and y.
If an element at the same key is present for both x and y, the value from y will appear in the result.
The merge is immutable, so neither x nor y will be modified.
I came across this article that seems to provide a pretty good solution. After going through the code and understanding it (for the most part), I shortened it down to the following:
var extend = function() {
var extended = {};
var length = arguments.length;
// Merge the object into the extended object
var merge = function(obj) {
for (var prop in obj) {
//Check if a property is an object and another layer of merging is required
if (Object.prototype.toString.call(obj[prop]) === '[object Object]') {
extended[prop] = extend(true, extended[prop], obj[prop]);
} else {
extended[prop] = obj[prop];
}
}
};
// Loop through each object and conduct a merge
while (length--) {
var obj = arguments[length];
merge(obj);
}
return extended;
};
From the original solution I removed the check for a deep merge as I would like to deep merge by default, and this line, present before the currently merged property value is checked for being an object:
if ( Object.prototype.hasOwnProperty.call( obj, prop ) )
I don't understand this line - why should we check if the object whose properties are currently being looped through has the property from the current loop? I feel like I'm missing something.
So that's it. Are there any cases where this function wouldn't fulfil my requirements? Or break execution on any other way? Thank you.
if ( Object.prototype.hasOwnProperty.call( obj, prop ) )
I don't understand this line - why should we check if the object whose properties are currently being looped through has the property from the current loop?
Because for-in loops visit all of the enumerable properties of an object, including ones it inherits from its prototype. Whether you want to copy inherited properties over depends on your use cases for your extend function. Apparently in the original code, they didn't want to.
Example showing the difference:
var name;
var p = {inherited: "property"};
// Create an object using p as its prototype
var o = Object.create(p);
o.own = "property";
console.log("Without check");
for (name in o) {
console.log("- " + name);
}
console.log("With check");
for (name in o) {
if (Object.prototype.hasOwnProperty.call(o, name)) {
console.log("- " + name);
}
}
Related
Say I want to assign a value like this:
x.label1.label2.label3 = someValue;
// or equivalently:
x['label1']['label2']['label3'] = someValue;
This works as long as x.label1.label2 is defined but runs into reference errors otherwise. Which makes sense of course. But is there an easy way to assign this anyway where it simply creates the necessary nested objects?
So for example, if x equals { label1: {}, otherLabel: 'otherValue' } I want to update x to become { label1: { label2: { label3: someValue } }, otherLabel: otherValue }
I think I might be able to write a function myself, but is there a language feature or standard library function that does this?
is there a language feature or standard library function that does this
No. You have to write your own function or use a library that provides such functionality.
Related: How to set object property (of object property of..) given its string name in JavaScript?
This is partially possible using the Proxy class. You can wrap your object in a Proxy and override the get trap to create another copy of the same proxy when you access a nonexistent property. This lets you recursively create "deep" properties. An example:
let traps = {
get: function (target, name) {
if (!(name in target))
target[name] = new Proxy({}, traps);
return target[name];
}
};
let x = new Proxy({}, traps);
Then you would use x like any object, except it has this special behavior:
x.label1.label2.label3 = 'foo';
which creates a nested hierarchy of objects. However, note that this will create an object even if you access a nonexistent property. Thus, you will have to use the in keyword to check if it really contains a given property.
I think you should indeed use a custom function such as:
function assignByPath(obj, path, value) {
var field = path.split('>'),
last = field.pop();
field.reduce(
function(node, f) {
return node[f] = node[f] instanceof Object ? node[f] : {};
}, obj
)[last] = value;
}
var myObj = {};
assignByPath(myObj, 'label1>label2>label3', 'someValue');
console.log(myObj);
Theoretically, you could also override Object.prototype, which would allow you to do:
myObj.assignByPath('label1>label2>label3', 'someValue');
But I would not recommend that.
You can use Array.prototype.shift(), Object.assign(), recursion
var x = {
label1: {},
otherLabel: "otherValue"
};
var nestprops = (props, value, obj, o, curr = props.shift()) => props.length
? nestprops(props, value, (Object.assign(obj, {[curr]: {}}) && obj[curr]), o)
: ((!value || value) && (obj[curr] = value) && o);
console.log(nestprops(["label1", "label2", "label3"], "someValue", x, x));
Check length of keys inside label1 object if its equal to 0 then modify it to your desired object.
Here is a snippet, hope it helps.
var obj = { label1: {}, otherLabel: 'otherValue' };
if(Object.keys(obj.label1).length == 0 ) {
obj.label1 = { label2: { label3: "value3" } };
}
console.log(obj);
This question already has answers here:
How do I loop through or enumerate a JavaScript object?
(48 answers)
Closed 6 years ago.
How do I enumerate the properties of a JavaScript object?
I actually want to list all the defined variables and their values, but I've learned that defining a variable actually creates a property of the window object.
Simple enough:
for(var propertyName in myObject) {
// propertyName is what you want
// you can get the value like this: myObject[propertyName]
}
Now, you will not get private variables this way because they are not available.
EDIT: #bitwiseplatypus is correct that unless you use the hasOwnProperty() method, you will get properties that are inherited - however, I don't know why anyone familiar with object-oriented programming would expect anything less! Typically, someone that brings this up has been subjected to Douglas Crockford's warnings about this, which still confuse me a bit. Again, inheritance is a normal part of OO languages and is therefore part of JavaScript, notwithstanding it being prototypical.
Now, that said, hasOwnProperty() is useful for filtering, but we don't need to sound a warning as if there is something dangerous in getting inherited properties.
EDIT 2: #bitwiseplatypus brings up the situation that would occur should someone add properties/methods to your objects at a point in time later than when you originally wrote your objects (via its prototype) - while it is true that this might cause unexpected behavior, I personally don't see that as my problem entirely. Just a matter of opinion. Besides, what if I design things in such a way that I use prototypes during the construction of my objects and yet have code that iterates over the properties of the object and I want all inherited properties? I wouldn't use hasOwnProperty(). Then, let's say, someone adds new properties later. Is that my fault if things behave badly at that point? I don't think so. I think this is why jQuery, as an example, has specified ways of extending how it works (via jQuery.extend and jQuery.fn.extend).
Use a for..in loop to enumerate an object's properties, but be careful. The enumeration will return properties not just of the object being enumerated, but also from the prototypes of any parent objects.
var myObject = {foo: 'bar'};
for (var name in myObject) {
alert(name);
}
// results in a single alert of 'foo'
Object.prototype.baz = 'quux';
for (var name in myObject) {
alert(name);
}
// results in two alerts, one for 'foo' and one for 'baz'
To avoid including inherited properties in your enumeration, check hasOwnProperty():
for (var name in myObject) {
if (myObject.hasOwnProperty(name)) {
alert(name);
}
}
Edit: I disagree with JasonBunting's statement that we don't need to worry about enumerating inherited properties. There is danger in enumerating over inherited properties that you aren't expecting, because it can change the behavior of your code.
It doesn't matter whether this problem exists in other languages; the fact is it exists, and JavaScript is particularly vulnerable since modifications to an object's prototype affects child objects even if the modification takes place after instantiation.
This is why JavaScript provides hasOwnProperty(), and this is why you should use it in order to ensure that third party code (or any other code that might modify a prototype) doesn't break yours. Apart from adding a few extra bytes of code, there is no downside to using hasOwnProperty().
The standard way, which has already been proposed several times is:
for (var name in myObject) {
alert(name);
}
However Internet Explorer 6, 7 and 8 have a bug in the JavaScript interpreter, which has the effect that some keys are not enumerated. If you run this code:
var obj = { toString: 12};
for (var name in obj) {
alert(name);
}
If will alert "12" in all browsers except IE. IE will simply ignore this key. The affected key values are:
isPrototypeOf
hasOwnProperty
toLocaleString
toString
valueOf
To be really safe in IE you have to use something like:
for (var key in myObject) {
alert(key);
}
var shadowedKeys = [
"isPrototypeOf",
"hasOwnProperty",
"toLocaleString",
"toString",
"valueOf"
];
for (var i=0, a=shadowedKeys, l=a.length; i<l; i++) {
if map.hasOwnProperty(a[i])) {
alert(a[i]);
}
}
The good news is that EcmaScript 5 defines the Object.keys(myObject) function, which returns the keys of an object as array and some browsers (e.g. Safari 4) already implement it.
In modern browsers (ECMAScript 5) to get all enumerable properties you can do:
Object.keys(obj)
(Check the link to get a snippet for backward compatibility on older browsers)
Or to get also non-enumerable properties:
Object.getOwnPropertyNames(obj)
Check ECMAScript 5 compatibility table
Additional info:
What is a enumerable attribute?
I think an example of the case that has caught me by surprise is relevant:
var myObject = { name: "Cody", status: "Surprised" };
for (var propertyName in myObject) {
document.writeln( propertyName + " : " + myObject[propertyName] );
}
But to my surprise, the output is
name : Cody
status : Surprised
forEach : function (obj, callback) {
for (prop in obj) {
if (obj.hasOwnProperty(prop) && typeof obj[prop] !== "function") {
callback(prop);
}
}
}
Why? Another script on the page has extended the Object prototype:
Object.prototype.forEach = function (obj, callback) {
for ( prop in obj ) {
if ( obj.hasOwnProperty( prop ) && typeof obj[prop] !== "function" ) {
callback( prop );
}
}
};
for (prop in obj) {
alert(prop + ' = ' + obj[prop]);
}
Simple JavaScript code:
for(var propertyName in myObject) {
// propertyName is what you want.
// You can get the value like this: myObject[propertyName]
}
jQuery:
jQuery.each(obj, function(key, value) {
// key is what you want.
// The value is in: value
});
Here's how to enumerate an object's properties:
var params = { name: 'myname', age: 'myage' }
for (var key in params) {
alert(key + "=" + params[key]);
}
I found it... for (property in object) { // do stuff } will list all the properties, and therefore all the globally declared variables on the window object..
You can use the for of loop.
If you want an array use:
Object.keys(object1)
Ref. Object.keys()
If you are using the Underscore.js library, you can use function keys:
_.keys({one : 1, two : 2, three : 3});
=> ["one", "two", "three"]
Python's dict has 'keys' method, and that is really useful. I think in JavaScript we can have something this:
function keys(){
var k = [];
for(var p in this) {
if(this.hasOwnProperty(p))
k.push(p);
}
return k;
}
Object.defineProperty(Object.prototype, "keys", { value : keys, enumerable:false });
EDIT: But the answer of #carlos-ruana works very well. I tested Object.keys(window), and the result is what I expected.
EDIT after 5 years: it is not good idea to extend Object, because it can conflict with other libraries that may want to use keys on their objects and it will lead unpredictable behavior on your project. #carlos-ruana answer is the correct way to get keys of an object.
If you're trying to enumerate the properties in order to write new code against the object, I would recommend using a debugger like Firebug to see them visually.
Another handy technique is to use Prototype's Object.toJSON() to serialize the object to JSON, which will show you both property names and values.
var data = {name: 'Violet', occupation: 'character', age: 25, pets: ['frog', 'rabbit']};
Object.toJSON(data);
//-> '{"name": "Violet", "occupation": "character", "age": 25, "pets": ["frog","rabbit"]}'
http://www.prototypejs.org/api/object/tojson
I'm still a beginner in JavaScript, but I wrote a small function to recursively print all the properties of an object and its children:
getDescription(object, tabs) {
var str = "{\n";
for (var x in object) {
str += Array(tabs + 2).join("\t") + x + ": ";
if (typeof object[x] === 'object' && object[x]) {
str += this.getDescription(object[x], tabs + 1);
} else {
str += object[x];
}
str += "\n";
}
str += Array(tabs + 1).join("\t") + "}";
return str;
}
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']
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.
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).