Related
I am working with an object where I need to preserve the order of the entries, even though some keys are alphanumeric and others are integers. (Yes, I know.)
The object I'm starting with looks like this:
{
a: 'the',
quick: 'quick',
b: 'brown',
fox: 'fox'
}
After manipulation, the object should look like this:
{
a: 'the',
0: 'quick',
b: 'brown',
1: 'fox'
}
But. Because iteration order in javascript objects differs from insertion order (integers are iterated first), if I go about this straightforwardly, I won't get the correctly ordered result:
let myReindexedObject = {};
myReindexedObject['a'] = 'the';
myReindexedObject['0'] = 'quick';
myReindexedObject['b'] = 'brown';
myReindexedObject['1'] = 'fox';
console.log(myReindexedObject);
I've tried to solve this issue by building a Map (which, unlike an object, preserves entry order) which I can then convert into an object.
Source: (I adapted this gist by Luke Horvat: Convert ES6 Map to Object Literal .)
Can you guess what happens?
let myMap = new Map();
myMap.set('a', 'the');
myMap.set('0', 'quick');
myMap.set('b', 'brown');
myMap.set('1', 'fox');
let myArray = Array.from(myMap);
let myReindexedObject = myArray.reduce((myReindexingObject, [key, value]) => {
return Object.assign(myReindexingObject, { [key]: value })
}, {});
console.log(myReindexedObject);
Is there any way I can use integer-based keys like 0 and 1 and still preserve the object entries in a custom order?
Or do I need to consider other approaches?
In the process of writing the question above, it suddenly occurred to me as I was typing:
(integers are iterated first)
that what a javascript engine recognises as an integer and what humans recognise as a number are, of course, not the same.
To any human, these two:
1
1.
are not typographically identical, but they are pretty much equivalent.
To any javascript interpreter, they are entirely distinct: the first is an integer; the second is not.
Working Example:
let myReindexedObject = {};
myReindexedObject['a'] = 'the';
myReindexedObject['0.'] = 'quick';
myReindexedObject['b'] = 'brown';
myReindexedObject['1.'] = 'fox';
console.log(myReindexedObject);
If the javascript interpreter needs to identify these indexes, it can do so, using the regex:
/d+\./
and, once identified, if it needs to know the integer that the string corresponds to, it can use:
parseInt(myIndex);
I will use this approach for now.
If anyone can suggest a better approach, I will be happy to upvote and accept.
We can define our own object, that keeps track of properties. And by intercepting required features we can make it work.
Using Proxy it's easily achievable:
// DEMO
let o = new CoolObject();
o['a'] = 'the';
o['0'] = 'quick';
o['b'] = 'brown';
o['1'] = 'fox';
o['c'] = 'jumped';
delete o['c'];
console.log('Object.keys: ', Object.keys(o));
console.log('JSON.stringify: ', JSON.stringify(o));
console.log('console.log: ', o);
console.log('Object.getOwnPropertyNames: ', Object.getOwnPropertyNames(o));
console.log('obj.propertyIsEnumerable("keys"): ', o.propertyIsEnumerable('keys'));
console.log('obj.propertyIsEnumerable("a"): ', o.propertyIsEnumerable('a'));
<script src="https://cdn.jsdelivr.net/gh/OnkarRuikar/temp#main/CoolObject.js"></script>
See console logs for output.
Note the insertion ordered property names. Result of getOwnPropertyNames are also insertion ordered except methods.
The CoolObject class definition:
(function () {
// original functions
let _keys = Object.keys;
let _getOwnPropertyNames = Object.getOwnPropertyNames;
let _defineProperty = Object.defineProperty;
let _stringify = JSON.stringify;
let _log = console.log;
// main feature definition
let CoolObject = function () {
let self = this;
let handler = {
_coolKeys: [],
set(target, key, val) {
let keys = this._coolKeys;
if (!keys.some(k => k === key))
keys.push(key);
target[key] = val;
},
get(target, key) {
return target[key];
},
keys() {
return this._coolKeys.slice(0);
},
deleteProperty(target, key) {
let keys = this._coolKeys;
const index = keys.indexOf(key);
if (index > -1) {
keys.splice(index, 1);
}
delete target[key];
},
defineProperty(obj, prop, val) {
let keys = this._coolKeys;
if (!keys.some(k => k === prop))
keys.push(prop);
_defineProperty(self, prop, val);
},
getOwnPropertyNames(obj) {
let props = _getOwnPropertyNames(obj);
return [...new Set([...this._coolKeys, ...props])];
},
// many improvements can be done here
// you can use your own modified pollyfill
stringifyHelper(obj, replacer, space) {
let out = '{';
for (let key of this._coolKeys) {
out += `"${key}":${_stringify(obj[key], replacer, space)}, `;
}
out += '}';
return out;
},
};
_defineProperty(self, 'keys', { value: () => handler.keys() });
_defineProperty(self, 'getOwnPropertyNames', { value: (o) => handler.getOwnPropertyNames(o) });
_defineProperty(self, 'stringify', { value: (...args) => handler.stringifyHelper(...args) });
return new Proxy(self, handler);
} // CoolObject end
// ----- wrap inbuilt objects -----
Object.keys = function (obj) {
if (!(obj instanceof CoolObject))
return _keys(obj);
return obj.keys();
}
Object.defineProperty = function (obj, prop, val) {
if (!(obj instanceof CoolObject))
_defineProperty(...arguments);
obj.defineProperty(...arguments);
}
Object.getOwnPropertyNames = function (obj) {
if (!(obj instanceof CoolObject))
return _getOwnPropertyNames(obj);
return obj.getOwnPropertyNames(obj);
}
JSON.stringify = function (obj, replacer, indent) {
if (!(obj instanceof CoolObject))
return _stringify(...arguments);
return obj.stringify(...arguments);
}
console.log = function () {
let myArgs = [];
for (let arg of arguments) {
if (arg instanceof CoolObject) {
let keys = arg.keys();
arg = Object.assign({}, arg);
for (let key of keys) {
arg[`.${key}`] = arg[key]
delete arg[key];
}
}
myArgs.push(arg);
}
_log(...myArgs);
}
window.CoolObject = CoolObject;
})();
The handler object maintains property names in _coolKeys array. And tracks addition and deletion operations. To make object behave like an original Object we need to wrap some inbuilt APIs, like Object.keys().
Note: for the demo I've implemented bare minimum rough code. Many improvements can be done. You can intercept more inbuilt APIs as per your requirements.
In JavaScript we have a few ways of getting the properties of an object, depending on what we want to get.
1) Object.keys(), which returns all own, enumerable properties of an object, an ECMA5 method.
2) a for...in loop, which returns all the enumerable properties of an object, regardless of whether they are own properties, or inherited from the prototype chain.
3) Object.getOwnPropertyNames(obj) which returns all own properties of an object, enumerable or not.
We also have such methods as hasOwnProperty(prop) lets us check if a property is inherited or actually belongs to that object, and propertyIsEnumerable(prop) which, as the name suggests, lets us check if a property is enumerable.
With all these options, there is no way to get a non-enumerable, non-own property of an object, which is what I want to do. Is there any way to do this? In other words, can I somehow get a list of the inherited non-enumerable properties?
Thank you.
Since getOwnPropertyNames can get you non-enumerable properties, you can use that and combine it with walking up the prototype chain.
function getAllProperties(obj){
var allProps = []
, curr = obj
do{
var props = Object.getOwnPropertyNames(curr)
props.forEach(function(prop){
if (allProps.indexOf(prop) === -1)
allProps.push(prop)
})
}while(curr = Object.getPrototypeOf(curr))
return allProps
}
console.log(getAllProperties([1,2,3]));
I tested that on Safari 5.1 and got
> getAllProperties([1,2,3])
["0", "1", "2", "length", "constructor", "push", "slice", "indexOf", "sort", "splice", "concat", "pop", "unshift", "shift", "join", "toString", "forEach", "reduceRight", "toLocaleString", "some", "map", "lastIndexOf", "reduce", "filter", "reverse", "every", "hasOwnProperty", "isPrototypeOf", "valueOf", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "propertyIsEnumerable", "__lookupSetter__"]
Update: Refactored the code a bit (added spaces, and curly braces, and improved the function name):
function getAllPropertyNames( obj ) {
var props = [];
do {
Object.getOwnPropertyNames( obj ).forEach(function ( prop ) {
if ( props.indexOf( prop ) === -1 ) {
props.push( prop );
}
});
} while ( obj = Object.getPrototypeOf( obj ) );
return props;
}
A cleaner solution using recursion:
function getAllPropertyNames (obj) {
const proto = Object.getPrototypeOf(obj);
const inherited = (proto) ? getAllPropertyNames(proto) : [];
return [...new Set(Object.getOwnPropertyNames(obj).concat(inherited))];
}
Edit
More generic functions:
function walkProtoChain (obj, callback) {
const proto = Object.getPrototypeOf(obj);
const inherited = (proto) ? walkProtoChain(proto, callback) : [];
return [...new Set(callback(obj).concat(inherited))];
}
function getOwnNonEnumPropertyNames (obj) {
return Object.getOwnPropertyNames(obj)
.filter(p => !obj.propertyIsEnumerable(p));
}
function getAllPropertyNames (obj) {
return walkProtoChain(obj, Object.getOwnPropertyNames);
}
function getAllEnumPropertyNames (obj) {
return walkProtoChain(obj, Object.keys);
}
function getAllNonEnumPropertyNames (obj) {
return walkProtoChain(obj, getOwnNonEnumPropertyNames);
}
This same template can be applied using Object.getOwnPropertySymbols, etc.
Update Jan 2022 -- almost every answer combined, plus symbols (ES6+)
After seeing Mozilla's JS documentation specifically say: "no single mechanism iterates all of an object's properties; the various mechanisms each include different subsets of properties."... I had exactly this question albeit newer because I want symbol keys also and I think all the answers above pre-date those).
I came here hoping someone else knew how to create such a single mechanism.
No single answer on this page seems to cover EVERYTHING, but between all of them, I think it can be done -- including the option to also exclude the annoying top level keys.
In exploring the code in Mozilla's JS doc'n that airportyh's answer inspired, plus the table below it on that same page I discovered the new Reflect.ownKeys. That catches everything (including symbols)... except inherited properties, but airportyh's answer walking the prototype chain fixes that.
So... combining all those findings and simplifying as much as I could, I came up with the following two functions, that (I believe) DO catch EVERYTHING. Posting in case it helps anyone else.
Option 1. Simple: Return EVERY Key, no exceptions
Returns every key, enumerable or not, string, symbol, own, inherited, and top level.
function getAllKeys(obj) {
let keys = [];
// if primitive (primitives still have keys) skip the first iteration
if (!(obj instanceof Object)) {
obj = Object.getPrototypeOf(obj)
}
while (obj) {
keys = keys.concat(Reflect.ownKeys(obj));
obj = Object.getPrototypeOf(obj);
}
return keys;
}
I really like the simplicity, though I wonder if I've missed anything. If any one catches any errors in that, please do let me know.
Option 2. Flexible: Return EVERY Key, with optional exclusions
Adds:
a filter function based on a bunch of one line functions (easier to debug that way, and this ain't code golf 😉) that determine if any given key should be excluded, based on the parameters passed in,
a condition to walk the prototype chain or not (per airportyh's answer), and,
a condition to stop or not before the top level is reached (per Maciej Krawczyk's answer).
Include or exclude:
enumerable keys
non-enumerable keys
symbol keys
string keys
own keys
inherited keys
top level keys.
(On a side note, I'm no JS expert, so maybe I'm missing something. I'm a little confused why no one else here has used Array.prototype.filter(), since isn't that exactly what we're doing?)
I believe the following covers it. By default everything is included except top level keys. Adjust to taste. Again I'd welcome feedback if any errors here:
function getAllKeysConditionally(obj, includeSelf = true, includePrototypeChain = true, includeTop = false, includeEnumerables = true, includeNonenumerables = true, includeStrings = true, includeSymbols = true) {
// Boolean (mini-)functions to determine any given key's eligibility:
const isEnumerable = (obj, key) => Object.propertyIsEnumerable.call(obj, key);
const isString = (key) => typeof key === 'string';
const isSymbol = (key) => typeof key === 'symbol';
const includeBasedOnEnumerability = (obj, key) => (includeEnumerables && isEnumerable(obj, key)) || (includeNonenumerables && !isEnumerable(obj, key));
const includeBasedOnKeyType = (key) => (includeStrings && isString(key)) || (includeSymbols && isSymbol(key));
const include = (obj, key) => includeBasedOnEnumerability(obj, key) && includeBasedOnKeyType(key);
const notYetRetrieved = (keys, key) => !keys.includes(key);
// filter function putting all the above together:
const filterFn = key => notYetRetrieved(keys, key) && include(obj, key);
// conditional chooses one of two functions to determine whether to exclude the top level or not:
const stopFn = includeTop ? (obj => obj === null) : (obj => Object.getPrototypeOf(obj) === null);
// and now the loop to collect and filter everything:
let keys = [];
while (!stopFn(obj, includeTop)) {
if (includeSelf) {
const ownKeys = Reflect.ownKeys(obj).filter(filterFn);
keys = keys.concat(ownKeys);
}
if (!includePrototypeChain) { break; }
else {
includeSelf = true;
obj = Object.getPrototypeOf(obj);
}
}
return keys;
}
As noted by Jeff Hykin in the comments these solutions use Reflect and arrow functions which are new in ES6. Therefore ES6 minimum required.
Straight forward iterative in ES6:
function getAllPropertyNames(obj) {
let result = new Set();
while (obj) {
Object.getOwnPropertyNames(obj).forEach(p => result.add(p));
obj = Object.getPrototypeOf(obj);
}
return [...result];
}
Example run:
function getAllPropertyNames(obj) {
let result = new Set();
while (obj) {
Object.getOwnPropertyNames(obj).forEach(p => result.add(p));
obj = Object.getPrototypeOf(obj);
}
return [...result];
}
let obj = {
abc: 123,
xyz: 1.234,
foobar: "hello"
};
console.log(getAllPropertyNames(obj));
Taking advantage of Sets leads to a somewhat cleaner solution, IMO.
const own = Object.getOwnPropertyNames;
const proto = Object.getPrototypeOf;
function getAllPropertyNames(obj) {
const props = new Set();
do own(obj).forEach(p => props.add(p)); while (obj = proto(obj));
return Array.from(props);
}
if you are trying to log non enumerable properties of a parent object ex. by default the methods defined inside a class in es6 are set on prototype but are set as non-enumerable.
Object.getOwnPropertyNames(Object.getPrototypeOf(obj));
To get all inherited properties or methods for some instance you could use something like this
var BaseType = function () {
this.baseAttribute = "base attribute";
this.baseMethod = function() {
return "base method";
};
};
var SomeType = function() {
BaseType();
this.someAttribute = "some attribute";
this.someMethod = function (){
return "some method";
};
};
SomeType.prototype = new BaseType();
SomeType.prototype.constructor = SomeType;
var instance = new SomeType();
Object.prototype.getInherited = function(){
var props = []
for (var name in this) {
if (!this.hasOwnProperty(name) && !(name == 'constructor' || name == 'getInherited')) {
props.push(name);
}
}
return props;
};
alert(instance.getInherited().join(","));
Here is the solution that I came up with while studying the subject. To get all non-enumerable non-own properties of the obj object do getProperties(obj, "nonown", "nonenum");
function getProperties(obj, type, enumerability) {
/**
* Return array of object properties
* #param {String} type - Property type. Can be "own", "nonown" or "both"
* #param {String} enumerability - Property enumerability. Can be "enum",
* "nonenum" or "both"
* #returns {String|Array} Array of properties
*/
var props = Object.create(null); // Dictionary
var firstIteration = true;
do {
var allProps = Object.getOwnPropertyNames(obj);
var enumProps = Object.keys(obj);
var nonenumProps = allProps.filter(x => !(new Set(enumProps)).has(x));
enumProps.forEach(function(prop) {
if (!(prop in props)) {
props[prop] = { own: firstIteration, enum_: true };
}
});
nonenumProps.forEach(function(prop) {
if (!(prop in props)) {
props[prop] = { own: firstIteration, enum_: false };
}
});
firstIteration = false;
} while (obj = Object.getPrototypeOf(obj));
for (prop in props) {
if (type == "own" && props[prop]["own"] == false) {
delete props[prop];
continue;
}
if (type == "nonown" && props[prop]["own"] == true) {
delete props[prop];
continue;
}
if (enumerability == "enum" && props[prop]["enum_"] == false) {
delete props[prop];
continue;
}
if (enumerability == "nonenum" && props[prop]["enum_"] == true) {
delete props[prop];
}
}
return Object.keys(props);
}
function getNonEnumerableNonOwnPropertyNames( obj ) {
var oCurObjPrototype = Object.getPrototypeOf(obj);
var arReturn = [];
var arCurObjPropertyNames = [];
var arCurNonEnumerable = [];
while (oCurObjPrototype) {
arCurObjPropertyNames = Object.getOwnPropertyNames(oCurObjPrototype);
arCurNonEnumerable = arCurObjPropertyNames.filter(function(item, i, arr){
return !oCurObjPrototype.propertyIsEnumerable(item);
})
Array.prototype.push.apply(arReturn,arCurNonEnumerable);
oCurObjPrototype = Object.getPrototypeOf(oCurObjPrototype);
}
return arReturn;
}
Example of using:
function MakeA(){
}
var a = new MakeA();
var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);
An implementation in my personal preferences :)
function getAllProperties(In, Out = {}) {
const keys = Object.getOwnPropertyNames(In);
keys.forEach(key => Object.defineProperty(In, key, {
enumerable: true
}));
Out = { ...In, ...Out };
const Prototype = Object.getPrototypeOf(In);
return Prototype === Object.prototype ? Out : getAllProperties(Proto, Out);
}
You usually don't want to include Object prototype properties such as __defineGetter__, hasOwnProperty, __proto__ and so on.
This implementation allows you to either include or exclude Object prototype properties:
function getAllPropertyNames(object, includeObjectPrototype = false) {
const props = Object.getOwnPropertyNames(object);
let proto = Object.getPrototypeOf(object);
const objectProto = Object.getPrototypeOf({});
while (proto && (includeObjectPrototype || proto !== objectProto)) {
for (const prop of Object.getOwnPropertyNames(proto)) {
if (props.indexOf(prop) === -1) {
props.push(prop);
}
}
proto = Object.getPrototypeOf(proto);
}
return props;
}
console.log(getAllPropertyNames(new Error('Test'), true));
// ["fileName", "lineNumber", "columnNumber", "message", "toString", "name", "stack", "constructor", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__"]
console.log(getAllPropertyNames(new Error('Test'), false));
// [ "fileName", "lineNumber", "columnNumber", "message", "toString", "name", "stack", "constructor" ]
In JavaScript we have a few ways of getting the properties of an object, depending on what we want to get.
1) Object.keys(), which returns all own, enumerable properties of an object, an ECMA5 method.
2) a for...in loop, which returns all the enumerable properties of an object, regardless of whether they are own properties, or inherited from the prototype chain.
3) Object.getOwnPropertyNames(obj) which returns all own properties of an object, enumerable or not.
We also have such methods as hasOwnProperty(prop) lets us check if a property is inherited or actually belongs to that object, and propertyIsEnumerable(prop) which, as the name suggests, lets us check if a property is enumerable.
With all these options, there is no way to get a non-enumerable, non-own property of an object, which is what I want to do. Is there any way to do this? In other words, can I somehow get a list of the inherited non-enumerable properties?
Thank you.
Since getOwnPropertyNames can get you non-enumerable properties, you can use that and combine it with walking up the prototype chain.
function getAllProperties(obj){
var allProps = []
, curr = obj
do{
var props = Object.getOwnPropertyNames(curr)
props.forEach(function(prop){
if (allProps.indexOf(prop) === -1)
allProps.push(prop)
})
}while(curr = Object.getPrototypeOf(curr))
return allProps
}
console.log(getAllProperties([1,2,3]));
I tested that on Safari 5.1 and got
> getAllProperties([1,2,3])
["0", "1", "2", "length", "constructor", "push", "slice", "indexOf", "sort", "splice", "concat", "pop", "unshift", "shift", "join", "toString", "forEach", "reduceRight", "toLocaleString", "some", "map", "lastIndexOf", "reduce", "filter", "reverse", "every", "hasOwnProperty", "isPrototypeOf", "valueOf", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "propertyIsEnumerable", "__lookupSetter__"]
Update: Refactored the code a bit (added spaces, and curly braces, and improved the function name):
function getAllPropertyNames( obj ) {
var props = [];
do {
Object.getOwnPropertyNames( obj ).forEach(function ( prop ) {
if ( props.indexOf( prop ) === -1 ) {
props.push( prop );
}
});
} while ( obj = Object.getPrototypeOf( obj ) );
return props;
}
A cleaner solution using recursion:
function getAllPropertyNames (obj) {
const proto = Object.getPrototypeOf(obj);
const inherited = (proto) ? getAllPropertyNames(proto) : [];
return [...new Set(Object.getOwnPropertyNames(obj).concat(inherited))];
}
Edit
More generic functions:
function walkProtoChain (obj, callback) {
const proto = Object.getPrototypeOf(obj);
const inherited = (proto) ? walkProtoChain(proto, callback) : [];
return [...new Set(callback(obj).concat(inherited))];
}
function getOwnNonEnumPropertyNames (obj) {
return Object.getOwnPropertyNames(obj)
.filter(p => !obj.propertyIsEnumerable(p));
}
function getAllPropertyNames (obj) {
return walkProtoChain(obj, Object.getOwnPropertyNames);
}
function getAllEnumPropertyNames (obj) {
return walkProtoChain(obj, Object.keys);
}
function getAllNonEnumPropertyNames (obj) {
return walkProtoChain(obj, getOwnNonEnumPropertyNames);
}
This same template can be applied using Object.getOwnPropertySymbols, etc.
Update Jan 2022 -- almost every answer combined, plus symbols (ES6+)
After seeing Mozilla's JS documentation specifically say: "no single mechanism iterates all of an object's properties; the various mechanisms each include different subsets of properties."... I had exactly this question albeit newer because I want symbol keys also and I think all the answers above pre-date those).
I came here hoping someone else knew how to create such a single mechanism.
No single answer on this page seems to cover EVERYTHING, but between all of them, I think it can be done -- including the option to also exclude the annoying top level keys.
In exploring the code in Mozilla's JS doc'n that airportyh's answer inspired, plus the table below it on that same page I discovered the new Reflect.ownKeys. That catches everything (including symbols)... except inherited properties, but airportyh's answer walking the prototype chain fixes that.
So... combining all those findings and simplifying as much as I could, I came up with the following two functions, that (I believe) DO catch EVERYTHING. Posting in case it helps anyone else.
Option 1. Simple: Return EVERY Key, no exceptions
Returns every key, enumerable or not, string, symbol, own, inherited, and top level.
function getAllKeys(obj) {
let keys = [];
// if primitive (primitives still have keys) skip the first iteration
if (!(obj instanceof Object)) {
obj = Object.getPrototypeOf(obj)
}
while (obj) {
keys = keys.concat(Reflect.ownKeys(obj));
obj = Object.getPrototypeOf(obj);
}
return keys;
}
I really like the simplicity, though I wonder if I've missed anything. If any one catches any errors in that, please do let me know.
Option 2. Flexible: Return EVERY Key, with optional exclusions
Adds:
a filter function based on a bunch of one line functions (easier to debug that way, and this ain't code golf 😉) that determine if any given key should be excluded, based on the parameters passed in,
a condition to walk the prototype chain or not (per airportyh's answer), and,
a condition to stop or not before the top level is reached (per Maciej Krawczyk's answer).
Include or exclude:
enumerable keys
non-enumerable keys
symbol keys
string keys
own keys
inherited keys
top level keys.
(On a side note, I'm no JS expert, so maybe I'm missing something. I'm a little confused why no one else here has used Array.prototype.filter(), since isn't that exactly what we're doing?)
I believe the following covers it. By default everything is included except top level keys. Adjust to taste. Again I'd welcome feedback if any errors here:
function getAllKeysConditionally(obj, includeSelf = true, includePrototypeChain = true, includeTop = false, includeEnumerables = true, includeNonenumerables = true, includeStrings = true, includeSymbols = true) {
// Boolean (mini-)functions to determine any given key's eligibility:
const isEnumerable = (obj, key) => Object.propertyIsEnumerable.call(obj, key);
const isString = (key) => typeof key === 'string';
const isSymbol = (key) => typeof key === 'symbol';
const includeBasedOnEnumerability = (obj, key) => (includeEnumerables && isEnumerable(obj, key)) || (includeNonenumerables && !isEnumerable(obj, key));
const includeBasedOnKeyType = (key) => (includeStrings && isString(key)) || (includeSymbols && isSymbol(key));
const include = (obj, key) => includeBasedOnEnumerability(obj, key) && includeBasedOnKeyType(key);
const notYetRetrieved = (keys, key) => !keys.includes(key);
// filter function putting all the above together:
const filterFn = key => notYetRetrieved(keys, key) && include(obj, key);
// conditional chooses one of two functions to determine whether to exclude the top level or not:
const stopFn = includeTop ? (obj => obj === null) : (obj => Object.getPrototypeOf(obj) === null);
// and now the loop to collect and filter everything:
let keys = [];
while (!stopFn(obj, includeTop)) {
if (includeSelf) {
const ownKeys = Reflect.ownKeys(obj).filter(filterFn);
keys = keys.concat(ownKeys);
}
if (!includePrototypeChain) { break; }
else {
includeSelf = true;
obj = Object.getPrototypeOf(obj);
}
}
return keys;
}
As noted by Jeff Hykin in the comments these solutions use Reflect and arrow functions which are new in ES6. Therefore ES6 minimum required.
Straight forward iterative in ES6:
function getAllPropertyNames(obj) {
let result = new Set();
while (obj) {
Object.getOwnPropertyNames(obj).forEach(p => result.add(p));
obj = Object.getPrototypeOf(obj);
}
return [...result];
}
Example run:
function getAllPropertyNames(obj) {
let result = new Set();
while (obj) {
Object.getOwnPropertyNames(obj).forEach(p => result.add(p));
obj = Object.getPrototypeOf(obj);
}
return [...result];
}
let obj = {
abc: 123,
xyz: 1.234,
foobar: "hello"
};
console.log(getAllPropertyNames(obj));
Taking advantage of Sets leads to a somewhat cleaner solution, IMO.
const own = Object.getOwnPropertyNames;
const proto = Object.getPrototypeOf;
function getAllPropertyNames(obj) {
const props = new Set();
do own(obj).forEach(p => props.add(p)); while (obj = proto(obj));
return Array.from(props);
}
if you are trying to log non enumerable properties of a parent object ex. by default the methods defined inside a class in es6 are set on prototype but are set as non-enumerable.
Object.getOwnPropertyNames(Object.getPrototypeOf(obj));
To get all inherited properties or methods for some instance you could use something like this
var BaseType = function () {
this.baseAttribute = "base attribute";
this.baseMethod = function() {
return "base method";
};
};
var SomeType = function() {
BaseType();
this.someAttribute = "some attribute";
this.someMethod = function (){
return "some method";
};
};
SomeType.prototype = new BaseType();
SomeType.prototype.constructor = SomeType;
var instance = new SomeType();
Object.prototype.getInherited = function(){
var props = []
for (var name in this) {
if (!this.hasOwnProperty(name) && !(name == 'constructor' || name == 'getInherited')) {
props.push(name);
}
}
return props;
};
alert(instance.getInherited().join(","));
Here is the solution that I came up with while studying the subject. To get all non-enumerable non-own properties of the obj object do getProperties(obj, "nonown", "nonenum");
function getProperties(obj, type, enumerability) {
/**
* Return array of object properties
* #param {String} type - Property type. Can be "own", "nonown" or "both"
* #param {String} enumerability - Property enumerability. Can be "enum",
* "nonenum" or "both"
* #returns {String|Array} Array of properties
*/
var props = Object.create(null); // Dictionary
var firstIteration = true;
do {
var allProps = Object.getOwnPropertyNames(obj);
var enumProps = Object.keys(obj);
var nonenumProps = allProps.filter(x => !(new Set(enumProps)).has(x));
enumProps.forEach(function(prop) {
if (!(prop in props)) {
props[prop] = { own: firstIteration, enum_: true };
}
});
nonenumProps.forEach(function(prop) {
if (!(prop in props)) {
props[prop] = { own: firstIteration, enum_: false };
}
});
firstIteration = false;
} while (obj = Object.getPrototypeOf(obj));
for (prop in props) {
if (type == "own" && props[prop]["own"] == false) {
delete props[prop];
continue;
}
if (type == "nonown" && props[prop]["own"] == true) {
delete props[prop];
continue;
}
if (enumerability == "enum" && props[prop]["enum_"] == false) {
delete props[prop];
continue;
}
if (enumerability == "nonenum" && props[prop]["enum_"] == true) {
delete props[prop];
}
}
return Object.keys(props);
}
function getNonEnumerableNonOwnPropertyNames( obj ) {
var oCurObjPrototype = Object.getPrototypeOf(obj);
var arReturn = [];
var arCurObjPropertyNames = [];
var arCurNonEnumerable = [];
while (oCurObjPrototype) {
arCurObjPropertyNames = Object.getOwnPropertyNames(oCurObjPrototype);
arCurNonEnumerable = arCurObjPropertyNames.filter(function(item, i, arr){
return !oCurObjPrototype.propertyIsEnumerable(item);
})
Array.prototype.push.apply(arReturn,arCurNonEnumerable);
oCurObjPrototype = Object.getPrototypeOf(oCurObjPrototype);
}
return arReturn;
}
Example of using:
function MakeA(){
}
var a = new MakeA();
var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);
An implementation in my personal preferences :)
function getAllProperties(In, Out = {}) {
const keys = Object.getOwnPropertyNames(In);
keys.forEach(key => Object.defineProperty(In, key, {
enumerable: true
}));
Out = { ...In, ...Out };
const Prototype = Object.getPrototypeOf(In);
return Prototype === Object.prototype ? Out : getAllProperties(Proto, Out);
}
You usually don't want to include Object prototype properties such as __defineGetter__, hasOwnProperty, __proto__ and so on.
This implementation allows you to either include or exclude Object prototype properties:
function getAllPropertyNames(object, includeObjectPrototype = false) {
const props = Object.getOwnPropertyNames(object);
let proto = Object.getPrototypeOf(object);
const objectProto = Object.getPrototypeOf({});
while (proto && (includeObjectPrototype || proto !== objectProto)) {
for (const prop of Object.getOwnPropertyNames(proto)) {
if (props.indexOf(prop) === -1) {
props.push(prop);
}
}
proto = Object.getPrototypeOf(proto);
}
return props;
}
console.log(getAllPropertyNames(new Error('Test'), true));
// ["fileName", "lineNumber", "columnNumber", "message", "toString", "name", "stack", "constructor", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__"]
console.log(getAllPropertyNames(new Error('Test'), false));
// [ "fileName", "lineNumber", "columnNumber", "message", "toString", "name", "stack", "constructor" ]
I have a for-of loop which goes like this:
for(const val of someArray[0].properties) {
// some processing;
}
Now for some reason if someArray[0].properties is undefined, the loop breaks, saying:
Cannot read property 'Symbol(Symbol.iterator)' of undefined
If I try to use the !! shorthand for boolean operators:
for (const val of !!someArray[0].properties && someArray[0].properties) {
}
it fails again.
The only solution I could come up with was:
if(someArray[0].properties){ // this will check for that undefined issue
for(const val of someArray[0].properties) {
// some processing;
}
}
Is there a more concise solution than this?
This is more concise:
for (const val of someArray[0].properties || []) {
// some processing
}
Basically, if someArray[0].properties is undefined, the empty array is used instead of it throwing an error.
Here are 3 that work for me. I prefer the 3rd loop as it's clearer.
Setting someArray.properties to null or undefined results in no loop and no error.
<script>
var someArray = [{ properties : [1,2] }]
for(const val of someArray[0].properties ? someArray[0].properties : []) {
console.log("1")
}
var props = someArray[0].properties
for(const val of props ? props : []) {
console.log("2")
}
for (const val of someArray[0].properties || []) {
console.log("3")
}
</script>
someArray[0].properties && Object.keys(someArray[0].properties).forEach(function(key){
var val = someArray[0].properties[key];
...
})
alternatively
for (const val of someArray[0].properties ? someArray[0].properties : {}) {
}
I think the best, simple and clear way is:
if (typeof someArray[0].properties !== 'undefined') {
for (const val of someArray[0].properties) {
//
}
}
The most common approach is using (maybe_null || {}).property, e.g.:
var empty = {};
((someArray || empty)[0] || empty).properties || empty
It's more concise if you use e instead of empty. :-) Or by using {} instead of a variable, which may increase the runtime cost by a tiny fraction.
This may not be the most clean solution but I guess it is what you're looking for:
//init
const someArray = [{
properties: {
a:'1',
b: 2,
c:undefined
}
}];
const props = someArray[0].properties;
for (const val of Object.keys(props).filter(x => props[x]).map(x => {
const a = {};
a[x] = props[x];
return a;
})) {
console.log(val);
}
Btw. I would not use this approach because it's kinda unreadable. Imo Vladimir Kovpak's answer is pretty straightforward which ends up in a much more maintainable code.
How do you deep clone a JavaScript object?
I know there are various functions based on frameworks like JSON.parse(JSON.stringify(o)) and $.extend(true, {}, o) but I don't want to use a framework like that.
What is the most elegant or efficient way to create a deep clone.
We do care about edge cases like cloning array's. Not breaking prototype chains, dealing with self reference.
We don't care about supporting copying of DOM objects and like because .cloneNode exists for that reason.
As I mainly want to use deep clones in node.js using ES5 features of the V8 engine is acceptable.
[Edit]
Before anyone suggests let me mention there is a distinct difference between creating a copy by prototypically inheriting from the object and cloning it. The former makes a mess of the prototype chain.
[Further Edit]
After reading your answer I came to the annoying discovery that cloning entire objects is a very dangerous and difficult game. Take for example the following closure based object
var o = (function() {
var magic = 42;
var magicContainer = function() {
this.get = function() { return magic; };
this.set = function(i) { magic = i; };
}
return new magicContainer;
}());
var n = clone(o); // how to implement clone to support closures
Is there any way to write a clone function that clones the object, has the same state at time of cloning but cannot alter the state of o without writing a JS parser in JS.
There should be no real world need for such a function anymore. This is mere academic interest.
Very simple way, maybe too simple:
var cloned = JSON.parse(JSON.stringify(objectToClone));
It really depends what you would like to clone. Is this a truly JSON object or just any object in JavaScript? If you would like to do any clone, it might get you into some trouble. Which trouble? I will explain it below, but first, a code example which clones object literals, any primitives, arrays and DOM nodes.
function clone(item) {
if (!item) { return item; } // null, undefined values check
var types = [ Number, String, Boolean ],
result;
// normalizing primitives if someone did new String('aaa'), or new Number('444');
types.forEach(function(type) {
if (item instanceof type) {
result = type( item );
}
});
if (typeof result == "undefined") {
if (Object.prototype.toString.call( item ) === "[object Array]") {
result = [];
item.forEach(function(child, index, array) {
result[index] = clone( child );
});
} else if (typeof item == "object") {
// testing that this is DOM
if (item.nodeType && typeof item.cloneNode == "function") {
result = item.cloneNode( true );
} else if (!item.prototype) { // check that this is a literal
if (item instanceof Date) {
result = new Date(item);
} else {
// it is an object literal
result = {};
for (var i in item) {
result[i] = clone( item[i] );
}
}
} else {
// depending what you would like here,
// just keep the reference, or create new object
if (false && item.constructor) {
// would not advice to do that, reason? Read below
result = new item.constructor();
} else {
result = item;
}
}
} else {
result = item;
}
}
return result;
}
var copy = clone({
one : {
'one-one' : new String("hello"),
'one-two' : [
"one", "two", true, "four"
]
},
two : document.createElement("div"),
three : [
{
name : "three-one",
number : new Number("100"),
obj : new function() {
this.name = "Object test";
}
}
]
})
And now, let's talk about problems you might get when start cloning REAL objects. I'm talking now, about objects which you create by doing something like
var User = function(){}
var newuser = new User();
Of course you can clone them, it's not a problem, every object expose constructor property, and you can use it to clone objects, but it will not always work. You also can do simple for in on this objects, but it goes to the same direction - trouble. I have also included clone functionality inside the code, but it's excluded by if( false ) statement.
So, why cloning can be a pain? Well, first of all, every object/instance might have some state. You never can be sure that your objects doesn't have for example an private variables, and if this is the case, by cloning object, you just break the state.
Imagine there is no state, that's fine. Then we still have another problem. Cloning via "constructor" method will give us another obstacle. It's an arguments dependency. You never can be sure, that someone who created this object, did not did, some kind of
new User({
bike : someBikeInstance
});
If this is the case, you are out of luck, someBikeInstance was probably created in some context and that context is unkown for clone method.
So what to do? You still can do for in solution, and treat such objects like normal object literals, but maybe it's an idea not to clone such objects at all, and just pass the reference of this object?
Another solution is - you could set a convention that all objects which must be cloned should implement this part by themselves and provide appropriate API method ( like cloneObject ). Something what cloneNode is doing for DOM.
You decide.
The JSON.parse(JSON.stringify()) combination to deep copy Javascript objects is an ineffective hack, as it was meant for JSON data. It does not support values of undefined or function () {}, and will simply ignore them (or null them) when "stringifying" (marshalling) the Javascript object into JSON.
A better solution is to use a deep copy function. The function below deep copies objects, and does not require a 3rd party library (jQuery, LoDash, etc).
function copy(aObject) {
// Prevent undefined objects
// if (!aObject) return aObject;
let bObject = Array.isArray(aObject) ? [] : {};
let value;
for (const key in aObject) {
// Prevent self-references to parent object
// if (Object.is(aObject[key], aObject)) continue;
value = aObject[key];
bObject[key] = (typeof value === "object") ? copy(value) : value;
}
return bObject;
}
Note: This code can check for simple self-references (uncomment the section // Prevent self-references to parent object), but you should also avoid creating objects with self-references when possible. Please see: https://softwareengineering.stackexchange.com/questions/11856/whats-wrong-with-circular-references
There is now structuredClone in the Web API which also works with circular references.
Previous answer
Here is an ES6 function that will also work for objects with cyclic references:
function deepClone(obj, hash = new WeakMap()) {
if (Object(obj) !== obj) return obj; // primitives
if (hash.has(obj)) return hash.get(obj); // cyclic reference
const result = obj instanceof Set ? new Set(obj) // See note about this!
: obj instanceof Map ? new Map(Array.from(obj, ([key, val]) =>
[key, deepClone(val, hash)]))
: obj instanceof Date ? new Date(obj)
: obj instanceof RegExp ? new RegExp(obj.source, obj.flags)
// ... add here any specific treatment for other classes ...
// and finally a catch-all:
: obj.constructor ? new obj.constructor()
: Object.create(null);
hash.set(obj, result);
return Object.assign(result, ...Object.keys(obj).map(
key => ({ [key]: deepClone(obj[key], hash) }) ));
}
// Sample data
var p = {
data: 1,
children: [{
data: 2,
parent: null
}]
};
p.children[0].parent = p;
var q = deepClone(p);
console.log(q.children[0].parent.data); // 1
A note about Sets and Maps
How to deal with the keys of Sets and Maps is debatable: those keys are often primitives (in which case there is no debate), but they can also be objects. In that case the question becomes: should those keys be cloned?
One could argue that this should be done, so that if those objects are mutated in the copy, the objects in the original are not affected, and vice versa.
On the other hand one would want that if a Set/Map has a key, this should be true in both the original and the copy -- at least before any change is made to either of them. It would be strange if the copy would be a Set/Map that has keys that never occurred before (as they were created during the cloning process): surely that is not very useful for any code that needs to know whether a given object is a key in that Set/Map or not.
As you notice, I am more of the second opinion: the keys of Sets and Maps are values (maybe references) that should remain the same.
Such choices will often also surface with other (maybe custom) objects. There is no general solution, as much depends on how the cloned object is expected to behave in your specific case.
we can achieve deep clone by using structuredClone()
const original = { name: "stack overflow" };
// Clone it
const clone = structuredClone(original);
The Underscore.js contrib library library has a function called snapshot that deep clones an object
snippet from the source:
snapshot: function(obj) {
if(obj == null || typeof(obj) != 'object') {
return obj;
}
var temp = new obj.constructor();
for(var key in obj) {
if (obj.hasOwnProperty(key)) {
temp[key] = _.snapshot(obj[key]);
}
}
return temp;
}
once the library is linked to your project, invoke the function simply using
_.snapshot(object);
Lo-Dash, now a superset of Underscore.js, has a couple of deep clone functions:
_.cloneDeep(object)
_.cloneDeepWith(object, (val) => {if(_.isElement(val)) return val.cloneNode(true)})
the second parameter is a function that is invoked to produce the cloned value.
From an answer of the author himself:
lodash underscore build is provided to ensure compatibility with the latest stable version of Underscore.
As others have noted on this and similar questions, cloning an "object", in the general sense, is dubious in JavaScript.
However, there is a class of objects, which I call "data" objects, that is, those constructed simply from { ... } literals and/or simple property assignments or deserialized from JSON for which it is reasonable to want to clone. Just today I wanted to artificially inflate data received from a server by 5x to test what happens for a large data set, but the object (an array) and its children had to be distinct objects for things to function correctly. Cloning allowed me to do this to multiply my data set:
return dta.concat(clone(dta),clone(dta),clone(dta),clone(dta));
The other place I often end up cloning data objects is for submitting data back to the host where I want to strip state fields from the object in the data model before sending it. For example, I might want to strip all fields starting with "_" from the object as it is cloned.
This is the code I ended up writing to do this generically, including supporting arrays and a selector to choose which members to clone (which uses a "path" string to determine context):
function clone(obj,sel) {
return (obj ? _clone("",obj,sel) : obj);
}
function _clone(pth,src,sel) {
var ret=(src instanceof Array ? [] : {});
for(var key in src) {
if(!src.hasOwnProperty(key)) { continue; }
var val=src[key], sub;
if(sel) {
sub+=pth+"/"+key;
if(!sel(sub,key,val)) { continue; }
}
if(val && typeof(val)=='object') {
if (val instanceof Boolean) { val=Boolean(val); }
else if(val instanceof Number ) { val=Number (val); }
else if(val instanceof String ) { val=String (val); }
else { val=_clone(sub,val,sel); }
}
ret[key]=val;
}
return ret;
}
The simplest reasonable deep-clone solution, assuming a non-null root object and with no member selection is:
function clone(src) {
var ret=(src instanceof Array ? [] : {});
for(var key in src) {
if(!src.hasOwnProperty(key)) { continue; }
var val=src[key];
if(val && typeof(val)=='object') { val=clone(val); }
ret[key]=val;
}
return ret;
}
This is the deep cloning method I use, I think it
Great, hope you make suggestions
function deepClone (obj) {
var _out = new obj.constructor;
var getType = function (n) {
return Object.prototype.toString.call(n).slice(8, -1);
}
for (var _key in obj) {
if (obj.hasOwnProperty(_key)) {
_out[_key] = getType(obj[_key]) === 'Object' || getType(obj[_key]) === 'Array' ? deepClone(obj[_key]) : obj[_key];
}
}
return _out;
}
The below function is most efficient way to deep clone javascript objects.
function deepCopy(obj){
if (!obj || typeof obj !== "object") return obj;
var retObj = {};
for (var attr in obj){
var type = obj[attr];
switch(true){
case (type instanceof Date):
var _d = new Date();
_d.setDate(type.getDate())
retObj[attr]= _d;
break;
case (type instanceof Function):
retObj[attr]= obj[attr];
break;
case (type instanceof Array):
var _a =[];
for (var e of type){
//_a.push(e);
_a.push(deepCopy(e));
}
retObj[attr]= _a;
break;
case (type instanceof Object):
var _o ={};
for (var e in type){
//_o[e] = type[e];
_o[e] = deepCopy(type[e]);
}
retObj[attr]= _o;
break;
default:
retObj[attr]= obj[attr];
}
}
return retObj;
}
var obj = {
string: 'test',
array: ['1'],
date: new Date(),
object:{c: 2, d:{e: 3}},
function: function(){
return this.date;
}
};
var copyObj = deepCopy(obj);
console.log('object comparison', copyObj === obj); //false
console.log('string check', copyObj.string === obj.string); //true
console.log('array check', copyObj.array === obj.array); //false
console.log('date check', copyObj2.date === obj.date); //false
console.log('object check', copyObj.object === obj.object); //false
console.log('function check', copyObj.function() === obj.function()); //true
Avoid use this method
let cloned = JSON.parse(JSON.stringify(objectToClone));
Why? this method will convert 'function,undefined' to null
const myObj = [undefined, null, function () {}, {}, '', true, false, 0, Symbol];
const IsDeepClone = JSON.parse(JSON.stringify(myObj));
console.log(IsDeepClone); //[null, null, null, {…}, "", true, false, 0, null]
try to use deepClone function.There are several above
There should be no real world need for such a function anymore. This is mere academic interest.
As purely an exercise, this is a more functional way of doing it. It's an extension of #tfmontague's answer as I'd suggested adding a guard block there. But seeing as I feel compelled to ES6 and functionalise all the things, here's my pimped version. It complicates the logic as you have to map over the array and reduce over the object, but it avoids any mutations.
const cloner = (x) => {
const recurseObj = x => (typeof x === 'object') ? cloner(x) : x
const cloneObj = (y, k) => {
y[k] = recurseObj(x[k])
return y
}
// Guard blocks
// Add extra for Date / RegExp if you want
if (!x) {
return x
}
if (Array.isArray(x)) {
return x.map(recurseObj)
}
return Object.keys(x).reduce(cloneObj, {})
}
const tests = [
null,
[],
{},
[1,2,3],
[1,2,3, null],
[1,2,3, null, {}],
[new Date('2001-01-01')], // FAIL doesn't work with Date
{x:'', y: {yx: 'zz', yy: null}, z: [1,2,3,null]},
{
obj : new function() {
this.name = "Object test";
}
} // FAIL doesn't handle functions
]
tests.map((x,i) => console.log(i, cloner(x)))
my addition to all the answers
function deepCopy(arr) {
if (typeof arr !== 'object') return arr
if (Array.isArray(arr)) return [...arr].map(deepCopy)
for (const prop in arr)
copy[prop] = deepCopy(arr[prop])
return copy
}
My solution, deep clones objects, arrays and functions.
let superClone = (object) => {
let cloning = {};
Object.keys(object).map(prop => {
if(Array.isArray(object[prop])) {
cloning[prop] = [].concat(object[prop])
} else if(typeof object[prop] === 'object') {
cloning[prop] = superClone(object[prop])
} else cloning[prop] = object[prop]
})
return cloning
}
example
let obj = {
a: 'a',
b: 'b',
c: {
deep: 'try and copy me',
d: {
deeper: 'try me again',
callDeeper() {
return this.deeper
}
},
arr: [1, 2, 3]
},
hi() {
return this.a
}
};
const cloned = superClone(obj)
obj.a = 'A'
obj.c.deep = 'i changed'
obj.c.arr = [45,454]
obj.c.d.deeper = 'i changed'
console.log(cloned) // unchanged object
If your objects contain methods don't use JSON to deep clone, JSON deep cloning doesn't clone methods.
If you take a look at this, object person2 only clones the name, not person1's greet method.
const person1 = {
name: 'John',
greet() {
return `HI, ${this.name}`
}
}
const person2 = JSON.parse(JSON.stringify(person1))
console.log(person2) // { name: 'John' }
Deep cloning of the object can be done in several ways but each having their own limitations as mentioned below. Hence, I will suggest you to use structuredClone algorithm.
JSON.parse(JSON.stringify(object)) - won't copy functions, Dates, undefineds & many more.
const obj = {
name: 'alpha',
printName: function() {
console.log(this.name);
}
};
console.log(JSON.parse(JSON.stringify(obj))); // function not copied
_.cloneDeep(object) - It is a good option but requires lodash.
const obj = {
name: 'alpha',
printName: function() {
console.log(this.name);
}
};
filteredArray = _.cloneDeep(obj);
console.log(filteredArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.2.1/lodash.min.js"></script>
structuredClone(object) - Browser Native API (It is good to use as JSON.parse() and JSON.stringify() do not serialize the circular object or things like Map, Set, Date, RegEx etc.)
const a = { x: 20, date: new Date() };
a.c = a;
console.log(structuredClone(a)); // { x: 20, date: <date object>, c: <circular ref> }
console.log(JSON.parse(JSON.stringify(a))); // throwing a TypeError
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;
}
This works for arrays, objects and primitives. Doubly recursive algorithm that switches between two traversal methods:
const deepClone = (objOrArray) => {
const copyArray = (arr) => {
let arrayResult = [];
arr.forEach(el => {
arrayResult.push(cloneObjOrArray(el));
});
return arrayResult;
}
const copyObj = (obj) => {
let objResult = {};
for (key in obj) {
if (obj.hasOwnProperty(key)) {
objResult[key] = cloneObjOrArray(obj[key]);
}
}
return objResult;
}
const cloneObjOrArray = (el) => {
if (Array.isArray(el)) {
return copyArray(el);
} else if (typeof el === 'object') {
return copyObj(el);
} else {
return el;
}
}
return cloneObjOrArray(objOrArray);
}
We can utilize recursion for making deepCopy. It can create copy of array, object, array of object, object with function.
if you want, you can add function for other type of data structure like map etc.
function deepClone(obj) {
var retObj;
_assignProps = function(obj, keyIndex, retObj) {
var subType = Object.prototype.toString.call(obj[keyIndex]);
if(subType === "[object Object]" || subType === "[object Array]") {
retObj[keyIndex] = deepClone(obj[keyIndex]);
}
else {
retObj[keyIndex] = obj[keyIndex];
}
};
if(Object.prototype.toString.call(obj) === "[object Object]") {
retObj = {};
for(key in obj) {
this._assignProps(obj, key, retObj);
}
}
else if(Object.prototype.toString.call(obj) == "[object Array]") {
retObj = [];
for(var i = 0; i< obj.length; i++) {
this._assignProps(obj, i, retObj);
}
};
return retObj;
};
Use immutableJS
import { fromJS } from 'immutable';
// An object we want to clone
let objA = {
a: { deep: 'value1', moreDeep: {key: 'value2'} }
};
let immB = fromJS(objA); // Create immutable Map
let objB = immB.toJS(); // Convert to plain JS object
console.log(objA); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }
console.log(objB); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }
// objA and objB are equalent, but now they and their inner objects are undependent
console.log(objA === objB); // false
console.log(objA.a === objB.a); // false
console.log(objA.moreDeep === objB.moreDeep); // false
Or lodash/merge
import merge from 'lodash/merge'
var objA = {
a: [{ 'b': 2 }, { 'd': 4 }]
};
// New deeply cloned object:
merge({}, objA );
// We can also create new object from several objects by deep merge:
var objB = {
a: [{ 'c': 3 }, { 'e': 5 }]
};
merge({}, objA , objB ); // Object { a: [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
This one, using circular reference, works for me
//a test-object with circular reference :
var n1 = { id:0, text:"aaaaa", parent:undefined}
var n2 = { id:1, text:"zzzzz", parent:undefined }
var o = { arr:[n1,n2], parent:undefined }
n1.parent = n2.parent = o;
var obj = { a:1, b:2, o:o }
o.parent = obj;
function deepClone(o,output){
if(!output) output = {};
if(o.______clone) return o.______clone;
o.______clone = output.______clone = output;
for(var z in o){
var obj = o[z];
if(typeof(obj) == "object") output[z] = deepClone(obj)
else output[z] = obj;
}
return output;
}
console.log(deepClone(obj));
var newDate = new Date(this.oldDate);
I was passing oldDate to function and generating newDate from this.oldDate, but it was changing this.oldDate also.So i used that solution and it worked.
This solution will avoid recursion problems when using [...target] or {...target}
function shallowClone(target) {
if (typeof a == 'array') return [...target]
if (typeof a == 'object') return {...target}
return target
}
/* set skipRecursion to avoid throwing an exception on recursive references */
/* no need to specify refs, or path -- they are used interally */
function deepClone(target, skipRecursion, refs, path) {
if (!refs) refs = []
if (!path) path = ''
if (refs.indexOf(target) > -1) {
if (skipRecursion) return null
throw('Recursive reference at ' + path)
}
refs.push(target)
let clone = shallowCopy(target)
for (i in target) target[i] = deepClone(target, refs, path + '.' + i)
return clone
}
Hello i just wanted to post my answer since i think its more readable. Note:this doesnt cover classes since i dont use them but you can easily add a condition for that
/** Copies any type of object/array of objects
* #param obj The object to be copied
* #param customKeys A list of keys that are to be excluded from deepCopy (optional)
*/
export function deepCopyObject(obj: any, customKeys?: Array<string|number|symbol>) {
if (obj == undefined)
return;
if (typeof obj !== 'object')
return obj;
if (typeof obj === 'function')
return obj;
const isArray = obj.length > -1;
if (isArray)
return copyArray(obj);
const isObjectDate = obj instanceof Date;
if(isObjectDate)
return new Date(obj);
const isDOM = obj.nodeType && typeof obj.cloneNode == "function";
if (isDOM)
return obj.cloneNode(true);
const isHtmlComponent = obj.$$typeof != undefined; // you can pass html/react components and maybe setup a custom function to copy them
if (isHtmlComponent)
return obj;
const newObject = <typeof obj>{};
const keys = Object.keys(obj);
keys.forEach((key: keyof (typeof obj)) => {
newObject[key] = copyKeysOfTypeObject(obj, key, customKeys);
})
const cantAccessObjectKeys = keys.lenght ==0; // ex: window.navigator
if (cantAccessObjectKeys)
return obj;
return newObject
}
function copyArray(arr: any) {
const newArr = new Array(0);
arr.forEach((obj: any) => {
newArr.push(deepCopyObject(obj));
})
return newArr;
}
function copyKeysOfTypeObject(obj: any, key: string | number | symbol, customKeys?: Array<string | number | symbol>) {
if (!key)
return;
if (customKeys && customKeys.includes(key))
return obj[key];
return deepCopyObject(obj[key]);
}
structuredClone now is supported by most the browsers
its main limitation is about DONT coping functions. It would require some extra work to copy/move it manually.
We can at least copy classes in an easy way by adding the prototypes later
const proto = Object.getPrototypeOf(object)
const newObject = structuredClone(object)
Object.setPrototypeOf(newObject, proto)
let obj1 = {
a: 100,
b: {
c: 200,
d: [1, 2, 3],
e: () => {}
}
}
function deepClone(obj) {
let newObj = {};
for (let key in obj) {
let val = obj[key];
if (val instanceof Array) {
newObj[key] = [...val]
} else if (typeof val === 'object') {
newObj[key] = deepClone(val)
} else {
newObj[key] = val;
}
}
return newObj;
}
obj2 = deepClone(obj1);
obj1.b.c = 300;
console.log(obj1);
console.log(obj2);