Unexpected variable behaviour [duplicate] - vue.js
This question's answers are a community effort. Edit existing answers to improve this post. It is not currently accepting new answers or interactions.
What is the most efficient way to clone a JavaScript object? I've seen obj = eval(uneval(o)); being used, but that's non-standard and only supported by Firefox. I've done things like obj = JSON.parse(JSON.stringify(o)); but question the efficiency. I've also seen recursive copying functions with various flaws.
I'm surprised no canonical solution exists.
Native deep cloning
There's now a JS standard called "structured cloning", that works experimentally in Node 11 and later, will land in browsers, and which has polyfills for existing systems.
structuredClone(value)
If needed, loading the polyfill first:
import structuredClone from '#ungap/structured-clone';
See this answer for more details.
Older answers
Fast cloning with data loss - JSON.parse/stringify
If you do not use Dates, functions, undefined, Infinity, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types within your object, a very simple one liner to deep clone an object is:
JSON.parse(JSON.stringify(object))
const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
re: /.*/, // lost
}
console.log(a);
console.log(typeof a.date); // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date); // result of .toISOString()
See Corban's answer for benchmarks.
Reliable cloning using a library
Since cloning objects is not trivial (complex types, circular references, function etc.), most major libraries provide function to clone objects. Don't reinvent the wheel - if you're already using a library, check if it has an object cloning function. For example,
lodash - cloneDeep; can be imported separately via the lodash.clonedeep module and is probably your best choice if you're not already using a library that provides a deep cloning function
AngularJS - angular.copy
jQuery - jQuery.extend(true, { }, oldObject); .clone() only clones DOM elements
just library - just-clone; Part of a library of zero-dependency npm modules that do just do one thing.
Guilt-free utilities for every occasion.
Checkout this benchmark: http://jsben.ch/#/bWfk9
In my previous tests where speed was a main concern I found
JSON.parse(JSON.stringify(obj))
to be the slowest way to deep clone an object (it is slower than jQuery.extend with deep flag set true by 10-20%).
jQuery.extend is pretty fast when the deep flag is set to false (shallow clone). It is a good option, because it includes some extra logic for type validation and doesn't copy over undefined properties, etc., but this will also slow you down a little.
If you know the structure of the objects you are trying to clone or can avoid deep nested arrays you can write a simple for (var i in obj) loop to clone your object while checking hasOwnProperty and it will be much much faster than jQuery.
Lastly if you are attempting to clone a known object structure in a hot loop you can get MUCH MUCH MORE PERFORMANCE by simply in-lining the clone procedure and manually constructing the object.
JavaScript trace engines suck at optimizing for..in loops and checking hasOwnProperty will slow you down as well. Manual clone when speed is an absolute must.
var clonedObject = {
knownProp: obj.knownProp,
..
}
Beware using the JSON.parse(JSON.stringify(obj)) method on Date objects - JSON.stringify(new Date()) returns a string representation of the date in ISO format, which JSON.parse() doesn't convert back to a Date object. See this answer for more details.
Additionally, please note that, in Chrome 65 at least, native cloning is not the way to go. According to JSPerf, performing native cloning by creating a new function is nearly 800x slower than using JSON.stringify which is incredibly fast all the way across the board.
Update for ES6
If you are using Javascript ES6 try this native method for cloning or shallow copy.
Object.assign({}, obj);
Structured Cloning
2022 update: The structuredClone global function is already available in Firefox 94, Node 17 and Deno 1.14
The HTML standard includes an internal structured cloning/serialization algorithm that can create deep clones of objects. It is still limited to certain built-in types, but in addition to the few types supported by JSON it also supports Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays, and probably more in the future. It also preserves references within the cloned data, allowing it to support cyclical and recursive structures that would cause errors for JSON.
Support in Node.js:
The structuredClone global function is provided by Node 17.0:
const clone = structuredClone(original);
Previous versions: The v8 module in Node.js (as of Node 11) exposes the structured serialization API directly, but this functionality is still marked as "experimental", and subject to change or removal in future versions. If you're using a compatible version, cloning an object is as simple as:
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};
Direct Support in Browsers: Available in Firefox 94
The structuredClone global function will soon be provided by all major browsers (having previously been discussed in whatwg/html#793 on GitHub). It looks / will look like this:
const clone = structuredClone(original);
Until this is shipped, browsers' structured clone implementations are only exposed indirectly.
Asynchronous Workaround: Usable. đ
The lower-overhead way to create a structured clone with existing APIs is to post the data through one port of a MessageChannels. The other port will emit a message event with a structured clone of the attached .data. Unfortunately, listening for these events is necessarily asynchronous, and the synchronous alternatives are less practical.
class StructuredCloner {
constructor() {
this.pendingClones_ = new Map();
this.nextKey_ = 0;
const channel = new MessageChannel();
this.inPort_ = channel.port1;
this.outPort_ = channel.port2;
this.outPort_.onmessage = ({data: {key, value}}) => {
const resolve = this.pendingClones_.get(key);
resolve(value);
this.pendingClones_.delete(key);
};
this.outPort_.start();
}
cloneAsync(value) {
return new Promise(resolve => {
const key = this.nextKey_++;
this.pendingClones_.set(key, resolve);
this.inPort_.postMessage({key, value});
});
}
}
const structuredCloneAsync = window.structuredCloneAsync =
StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);
Example Use:
const main = async () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = await structuredCloneAsync(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
main();
Synchronous Workarounds: Awful! đ¤˘
There are no good options for creating structured clones synchronously. Here are a couple of impractical hacks instead.
history.pushState() and history.replaceState() both create a structured clone of their first argument, and assign that value to history.state. You can use this to create a structured clone of any object like this:
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
Example Use:
'use strict';
const main = () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = structuredClone(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
main();
Though synchronous, this can be extremely slow. It incurs all of the overhead associated with manipulating the browser history. Calling this method repeatedly can cause Chrome to become temporarily unresponsive.
The Notification constructor creates a structured clone of its associated data. It also attempts to display a browser notification to the user, but this will silently fail unless you have requested notification permission. In case you have the permission for other purposes, we'll immediately close the notification we've created.
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.onshow = n.close.bind(n);
return n.data;
};
Example Use:
'use strict';
const main = () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = structuredClone(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.close();
return n.data;
};
main();
Assuming that you have only properties and not any functions in your object, you can just use:
var newObject = JSON.parse(JSON.stringify(oldObject));
If there wasn't any builtin one, you could try:
function clone(obj) {
if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
return obj;
if (obj instanceof Date)
var temp = new obj.constructor(); //or new Date(obj);
else
var temp = obj.constructor();
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
obj['isActiveClone'] = null;
temp[key] = clone(obj[key]);
delete obj['isActiveClone'];
}
}
return temp;
}
The efficient way to clone(not deep-clone) an object in one line of code
An Object.assign method is part of the ECMAScript 2015 (ES6) standard and does exactly what you need.
var clone = Object.assign({}, obj);
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.
Read more...
The polyfill to support older browsers:
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
Deep copy by performance:
Ranked from best to worst
spread operator ... (primitive arrays - only)
splice(0) (primitive arrays - only)
slice() (primitive arrays - only)
concat() (primitive arrays - only)
custom function, as seen below (any array)
jQuery's $.extend() (any array)
JSON.parse(JSON.stringify()) (primitive and literal arrays - only)
Underscore's _.clone() (primitive and literal arrays - only)
Lodash's _.cloneDeep() (any array)
Where:
primitives = strings, numbers, and booleans
literals = object literals {}, array literals []
any = primitives, literals, and prototypes
Deep copy an array of primitives:
let arr1a = [1, 'a', true];
To deep copy arrays with primitives only (i.e. numbers, strings, and booleans), reassignment, slice(), concat(), and Underscore's clone() can be used.
Where spread has the fastest performance:
let arr1b = [...arr1a];
And where slice() has better performance than concat(): https://jsbench.me/x5ktn7o94d/
let arr1c = arr1a.splice(0);
let arr1d = arr1a.slice();
let arr1e = arr1a.concat();
Deep copy an array of primitive and object literals:
let arr2a = [1, 'a', true, {}, []];
let arr2b = JSON.parse(JSON.stringify(arr2a));
Deep copy an array of primitive, object literals, and prototypes:
let arr3a = [1, 'a', true, {}, [], new Object()];
Write a custom function (has faster performance than $.extend() or JSON.parse):
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;
}
let arr3b = copy(arr3a);
Or use third-party utility functions:
let arr3c = $.extend(true, [], arr3a); // jQuery Extend
let arr3d = _.cloneDeep(arr3a); // Lodash
Note: jQuery's $.extend also has better performance than JSON.parse(JSON.stringify()):
js-deep-copy
jquery-extend-vs-json-parse
This is what I'm using:
function cloneObject(obj) {
var clone = {};
for(var i in obj) {
if(typeof(obj[i])=="object" && obj[i] != null)
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
Code:
// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
if (from == null || typeof from != "object") return from;
if (from.constructor != Object && from.constructor != Array) return from;
if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
from.constructor == String || from.constructor == Number || from.constructor == Boolean)
return new from.constructor(from);
to = to || new from.constructor();
for (var name in from)
{
to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
}
return to;
}
Test:
var obj =
{
date: new Date(),
func: function(q) { return 1 + q; },
num: 123,
text: "asdasd",
array: [1, "asd"],
regex: new RegExp(/aaa/i),
subobj:
{
num: 234,
text: "asdsaD"
}
}
var clone = extend(obj);
Deep copying objects in JavaScript (I think the best and the simplest)
1. Using JSON.parse(JSON.stringify(object));
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
2.Using created method
function cloneObject(obj) {
var clone = {};
for(var i in obj) {
if(obj[i] != null && typeof(obj[i])=="object")
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = cloneObject(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
3. Using Lo-Dash's _.cloneDeep link lodash
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
4. Using Object.assign() method
var obj = {
a: 1,
b: 2
}
var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }
BUT WRONG WHEN
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.
5.Using Underscore.js _.clone link Underscore.js
var obj = {
a: 1,
b: 2
}
var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }
BUT WRONG WHEN
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)
JSBEN.CH Performance Benchmarking Playground 1~3 http://jsben.ch/KVQLd
Cloning an object was always a concern in JS, but it was all about before ES6, I list different ways of copying an object in JavaScript below, imagine you have the Object below and would like to have a deep copy of that:
var obj = {a:1, b:2, c:3, d:4};
There are few ways to copy this object, without changing the origin:
ES5+, Using a simple function to do the copy for you:
function deepCopyObj(obj) {
if (null == obj || "object" != typeof obj) return obj;
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = deepCopyObj(obj[i]);
}
return copy;
}
if (obj instanceof Object) {
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = deepCopyObj(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj this object.");
}
ES5+, using JSON.parse and JSON.stringify.
var deepCopyObj = JSON.parse(JSON.stringify(obj));
Angular:
var deepCopyObj = angular.copy(obj);
jQuery:
var deepCopyObj = jQuery.extend(true, {}, obj);
Underscore.js & Lodash:
var deepCopyObj = _.cloneDeep(obj); //latest version of Underscore.js makes shallow copy
Hope these helpâŚ
var clone = function() {
var newObj = (this instanceof Array) ? [] : {};
for (var i in this) {
if (this[i] && typeof this[i] == "object") {
newObj[i] = this[i].clone();
}
else
{
newObj[i] = this[i];
}
}
return newObj;
};
Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});
Thereâs a library (called âcloneâ), that does this quite well. It provides the most complete recursive cloning/copying of arbitrary objects that I know of. It also supports circular references, which is not covered by the other answers, yet.
You can find it on npm, too. It can be used for the browser as well as Node.js.
Here is an example on how to use it:
Install it with
npm install clone
or package it with Ender.
ender build clone [...]
You can also download the source code manually.
Then you can use it in your source code.
var clone = require('clone');
var a = { foo: { bar: 'baz' } }; // inital value of a
var b = clone(a); // clone a -> b
a.foo.bar = 'foo'; // change a
console.log(a); // { foo: { bar: 'foo' } }
console.log(b); // { foo: { bar: 'baz' } }
(Disclaimer: Iâm the author of the library.)
I know this is an old post, but I thought this may be of some help to the next person who stumbles along.
As long as you don't assign an object to anything it maintains no reference in memory. So to make an object that you want to share among other objects, you'll have to create a factory like so:
var a = function(){
return {
father:'zacharias'
};
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);
If you're using it, the Underscore.js library has a clone method.
var newObject = _.clone(oldObject);
Here's a version of ConroyP's answer above that works even if the constructor has required parameters:
//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
object_create = function(o) {
function F() {}
F.prototype = o;
return new F();
};
}
function deepCopy(obj) {
if(obj == null || typeof(obj) !== 'object'){
return obj;
}
//make sure the returned object has the same prototype as the original
var ret = object_create(obj.constructor.prototype);
for(var key in obj){
ret[key] = deepCopy(obj[key]);
}
return ret;
}
This function is also available in my simpleoo library.
Edit:
Here's a more robust version (thanks to Justin McCandless this now supports cyclic references as well):
/**
* Deep copy an object (make copies of all its object properties, sub-properties, etc.)
* An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
* that doesn't break if the constructor has required parameters
*
* It also borrows some code from http://stackoverflow.com/a/11621004/560114
*/
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
if(src === null || typeof(src) !== 'object'){
return src;
}
//Honor native/custom clone methods
if(typeof src.clone == 'function'){
return src.clone(true);
}
//Special cases:
//Date
if(src instanceof Date){
return new Date(src.getTime());
}
//RegExp
if(src instanceof RegExp){
return new RegExp(src);
}
//DOM Element
if(src.nodeType && typeof src.cloneNode == 'function'){
return src.cloneNode(true);
}
// Initialize the visited objects arrays if needed.
// This is used to detect cyclic references.
if (_visited === undefined){
_visited = [];
_copiesVisited = [];
}
// Check if this object has already been visited
var i, len = _visited.length;
for (i = 0; i < len; i++) {
// If so, get the copy we already made
if (src === _visited[i]) {
return _copiesVisited[i];
}
}
//Array
if (Object.prototype.toString.call(src) == '[object Array]') {
//[].slice() by itself would soft clone
var ret = src.slice();
//add it to the visited array
_visited.push(src);
_copiesVisited.push(ret);
var i = ret.length;
while (i--) {
ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
}
return ret;
}
//If we've reached here, we have a regular object
//make sure the returned object has the same prototype as the original
var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
if (!proto) {
proto = src.constructor.prototype; //this line would probably only be reached by very old browsers
}
var dest = object_create(proto);
//add this object to the visited array
_visited.push(src);
_copiesVisited.push(dest);
for (var key in src) {
//Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
//For an example of how this could be modified to do so, see the singleMixin() function
dest[key] = deepCopy(src[key], _visited, _copiesVisited);
}
return dest;
}
//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
object_create = function(o) {
function F() {}
F.prototype = o;
return new F();
};
}
The following creates two instances of the same object. I found it and am using it currently. It's simple and easy to use.
var objToCreate = JSON.parse(JSON.stringify(cloneThis));
Crockford suggests (and I prefer) using this function:
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var newObject = object(oldObject);
It's terse, works as expected and you don't need a library.
EDIT:
This is a polyfill for Object.create, so you also can use this.
var newObject = Object.create(oldObject);
NOTE: If you use some of this, you may have problems with some iteration who use hasOwnProperty. Because, create create new empty object who inherits oldObject. But it is still useful and practical for cloning objects.
For exemple if oldObject.a = 5;
newObject.a; // is 5
but:
oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false
function clone(obj)
{ var clone = {};
clone.prototype = obj.prototype;
for (property in obj) clone[property] = obj[property];
return clone;
}
Lodash has a nice _.cloneDeep(value) method:
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
Shallow copy one-liner (ECMAScript 5th edition):
var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});
console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
And shallow copy one-liner (ECMAScript 6th edition, 2015):
var origin = { foo : {} };
var copy = Object.assign({}, origin);
console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
There seems to be no ideal deep clone operator yet for array-like objects. As the code below illustrates, John Resig's jQuery cloner turns arrays with non-numeric properties into objects that are not arrays, and RegDwight's JSON cloner drops the non-numeric properties. The following tests illustrate these points on multiple browsers:
function jQueryClone(obj) {
return jQuery.extend(true, {}, obj)
}
function JSONClone(obj) {
return JSON.parse(JSON.stringify(obj))
}
var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);
alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
"\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
"\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
"\nAnd what are the JSONClone names? " + JSONCopy.names)
Just because I didn't see AngularJS mentioned and thought that people might want to know...
angular.copy also provides a method of deep copying objects and arrays.
I have two good answers depending on whether your objective is to clone a "plain old JavaScript object" or not.
Let's also assume that your intention is to create a complete clone with no prototype references back to the source object. If you're not interested in a complete clone, then you can use many of the Object.clone() routines provided in some of the other answers (Crockford's pattern).
For plain old JavaScript objects, a tried and true good way to clone an object in modern runtimes is quite simply:
var clone = JSON.parse(JSON.stringify(obj));
Note that the source object must be a pure JSON object. This is to say, all of its nested properties must be scalars (like boolean, string, array, object, etc). Any functions or special objects like RegExp or Date will not be cloned.
Is it efficient? Heck yes. We've tried all kinds of cloning methods and this works best. I'm sure some ninja could conjure up a faster method. But I suspect we're talking about marginal gains.
This approach is just simple and easy to implement. Wrap it into a convenience function and if you really need to squeeze out some gain, go for at a later time.
Now, for non-plain JavaScript objects, there isn't a really simple answer. In fact, there can't be because of the dynamic nature of JavaScript functions and inner object state. Deep cloning a JSON structure with functions inside requires you recreate those functions and their inner context. And JavaScript simply doesn't have a standardized way of doing that.
The correct way to do this, once again, is via a convenience method that you declare and reuse within your code. The convenience method can be endowed with some understanding of your own objects so you can make sure to properly recreate the graph within the new object.
We're written our own, but the best general approach I've seen is covered here:
http://davidwalsh.name/javascript-clone
This is the right idea. The author (David Walsh) has commented out the cloning of generalized functions. This is something you might choose to do, depending on your use case.
The main idea is that you need to special handle the instantiation of your functions (or prototypal classes, so to speak) on a per-type basis. Here, he's provided a few examples for RegExp and Date.
Not only is this code brief, but it's also very readable. It's pretty easy to extend.
Is this efficient? Heck yes. Given that the goal is to produce a true deep-copy clone, then you're going to have to walk the members of the source object graph. With this approach, you can tweak exactly which child members to treat and how to manually handle custom types.
So there you go. Two approaches. Both are efficient in my view.
I am late to answer this question, but I have an another way of cloning the object:
function cloneObject(obj) {
if (obj === null || typeof(obj) !== 'object')
return obj;
var temp = obj.constructor(); // changed
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
obj['isActiveClone'] = null;
temp[key] = cloneObject(obj[key]);
delete obj['isActiveClone'];
}
}
return temp;
}
var b = cloneObject({"a":1,"b":2}); // calling
which is much better and faster then:
var a = {"a":1,"b":2};
var b = JSON.parse(JSON.stringify(a));
and
var a = {"a":1,"b":2};
// Deep copy
var newObject = jQuery.extend(true, {}, a);
I have bench-marked the code and you can test the results here:
and sharing the results:
References: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
Only when you can use ECMAScript 6 or transpilers.
Features:
Won't trigger getter/setter while copying.
Preserves getter/setter.
Preserves prototype informations.
Works with both object-literal and functional OO writing styles.
Code:
function clone(target, source){
for(let key in source){
// Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter.
let descriptor = Object.getOwnPropertyDescriptor(source, key);
if(descriptor.value instanceof String){
target[key] = new String(descriptor.value);
}
else if(descriptor.value instanceof Array){
target[key] = clone([], descriptor.value);
}
else if(descriptor.value instanceof Object){
let prototype = Reflect.getPrototypeOf(descriptor.value);
let cloneObject = clone({}, descriptor.value);
Reflect.setPrototypeOf(cloneObject, prototype);
target[key] = cloneObject;
}
else {
Object.defineProperty(target, key, descriptor);
}
}
let prototype = Reflect.getPrototypeOf(source);
Reflect.setPrototypeOf(target, prototype);
return target;
}
This isn't generally the most efficient solution, but it does what I need. Simple test cases below...
function clone(obj, clones) {
// Makes a deep copy of 'obj'. Handles cyclic structures by
// tracking cloned obj's in the 'clones' parameter. Functions
// are included, but not cloned. Functions members are cloned.
var new_obj,
already_cloned,
t = typeof obj,
i = 0,
l,
pair;
clones = clones || [];
if (obj === null) {
return obj;
}
if (t === "object" || t === "function") {
// check to see if we've already cloned obj
for (i = 0, l = clones.length; i < l; i++) {
pair = clones[i];
if (pair[0] === obj) {
already_cloned = pair[1];
break;
}
}
if (already_cloned) {
return already_cloned;
} else {
if (t === "object") { // create new object
new_obj = new obj.constructor();
} else { // Just use functions as is
new_obj = obj;
}
clones.push([obj, new_obj]); // keep track of objects we've cloned
for (key in obj) { // clone object members
if (obj.hasOwnProperty(key)) {
new_obj[key] = clone(obj[key], clones);
}
}
}
}
return new_obj || obj;
}
Cyclic array test...
a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true
Function test...
f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false
For the people who want to use the JSON.parse(JSON.stringify(obj)) version, but without losing the Date objects, you can use the second argument of parse method to convert the strings back to Date:
function clone(obj) {
var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
return JSON.parse(JSON.stringify(obj), function(k, v) {
if (typeof v === 'string' && regExp.test(v))
return new Date(v)
return v;
})
}
// usage:
var original = {
a: [1, null, undefined, 0, {a:null}, new Date()],
b: {
c(){ return 0 }
}
}
var cloned = clone(original)
console.log(cloned)
I disagree with the answer with the greatest votes here. A Recursive Deep Clone is much faster than the JSON.parse(JSON.stringify(obj)) approach mentioned.
Jsperf ranks it number one here: https://jsperf.com/deep-copy-vs-json-stringify-json-parse/5
Jsben from the answer above updated to show that a recursive deep clone beats all the others mentioned: http://jsben.ch/13YKQ
And here's the function for quick reference:
function cloneDeep (o) {
let newO
let i
if (typeof o !== 'object') return o
if (!o) return o
if (Object.prototype.toString.apply(o) === '[object Array]') {
newO = []
for (i = 0; i < o.length; i += 1) {
newO[i] = cloneDeep(o[i])
}
return newO
}
newO = {}
for (i in o) {
if (o.hasOwnProperty(i)) {
newO[i] = cloneDeep(o[i])
}
}
return newO
}
Here is a comprehensive clone() method that can clone any JavaScript object. It handles almost all the cases:
function clone(src, deep) {
var toString = Object.prototype.toString;
if (!src && typeof src != "object") {
// Any non-object (Boolean, String, Number), null, undefined, NaN
return src;
}
// Honor native/custom clone methods
if (src.clone && toString.call(src.clone) == "[object Function]") {
return src.clone(deep);
}
// DOM elements
if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") {
return src.cloneNode(deep);
}
// Date
if (toString.call(src) == "[object Date]") {
return new Date(src.getTime());
}
// RegExp
if (toString.call(src) == "[object RegExp]") {
return new RegExp(src);
}
// Function
if (toString.call(src) == "[object Function]") {
//Wrap in another method to make sure == is not true;
//Note: Huge performance issue due to closures, comment this :)
return (function(){
src.apply(this, arguments);
});
}
var ret, index;
//Array
if (toString.call(src) == "[object Array]") {
//[].slice(0) would soft clone
ret = src.slice();
if (deep) {
index = ret.length;
while (index--) {
ret[index] = clone(ret[index], true);
}
}
}
//Object
else {
ret = src.constructor ? new src.constructor() : {};
for (var prop in src) {
ret[prop] = deep
? clone(src[prop], true)
: src[prop];
}
}
return ret;
};
Related
Properties of Object Changed when another Object changed that is the copy of Object in VUEJS [duplicate]
This question's answers are a community effort. Edit existing answers to improve this post. It is not currently accepting new answers or interactions. What is the most efficient way to clone a JavaScript object? I've seen obj = eval(uneval(o)); being used, but that's non-standard and only supported by Firefox. I've done things like obj = JSON.parse(JSON.stringify(o)); but question the efficiency. I've also seen recursive copying functions with various flaws. I'm surprised no canonical solution exists.
Native deep cloning There's now a JS standard called "structured cloning", that works experimentally in Node 11 and later, will land in browsers, and which has polyfills for existing systems. structuredClone(value) If needed, loading the polyfill first: import structuredClone from '#ungap/structured-clone'; See this answer for more details. Older answers Fast cloning with data loss - JSON.parse/stringify If you do not use Dates, functions, undefined, Infinity, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types within your object, a very simple one liner to deep clone an object is: JSON.parse(JSON.stringify(object)) const a = { string: 'string', number: 123, bool: false, nul: null, date: new Date(), // stringified undef: undefined, // lost inf: Infinity, // forced to 'null' re: /.*/, // lost } console.log(a); console.log(typeof a.date); // Date object const clone = JSON.parse(JSON.stringify(a)); console.log(clone); console.log(typeof clone.date); // result of .toISOString() See Corban's answer for benchmarks. Reliable cloning using a library Since cloning objects is not trivial (complex types, circular references, function etc.), most major libraries provide function to clone objects. Don't reinvent the wheel - if you're already using a library, check if it has an object cloning function. For example, lodash - cloneDeep; can be imported separately via the lodash.clonedeep module and is probably your best choice if you're not already using a library that provides a deep cloning function AngularJS - angular.copy jQuery - jQuery.extend(true, { }, oldObject); .clone() only clones DOM elements just library - just-clone; Part of a library of zero-dependency npm modules that do just do one thing. Guilt-free utilities for every occasion.
Checkout this benchmark: http://jsben.ch/#/bWfk9 In my previous tests where speed was a main concern I found JSON.parse(JSON.stringify(obj)) to be the slowest way to deep clone an object (it is slower than jQuery.extend with deep flag set true by 10-20%). jQuery.extend is pretty fast when the deep flag is set to false (shallow clone). It is a good option, because it includes some extra logic for type validation and doesn't copy over undefined properties, etc., but this will also slow you down a little. If you know the structure of the objects you are trying to clone or can avoid deep nested arrays you can write a simple for (var i in obj) loop to clone your object while checking hasOwnProperty and it will be much much faster than jQuery. Lastly if you are attempting to clone a known object structure in a hot loop you can get MUCH MUCH MORE PERFORMANCE by simply in-lining the clone procedure and manually constructing the object. JavaScript trace engines suck at optimizing for..in loops and checking hasOwnProperty will slow you down as well. Manual clone when speed is an absolute must. var clonedObject = { knownProp: obj.knownProp, .. } Beware using the JSON.parse(JSON.stringify(obj)) method on Date objects - JSON.stringify(new Date()) returns a string representation of the date in ISO format, which JSON.parse() doesn't convert back to a Date object. See this answer for more details. Additionally, please note that, in Chrome 65 at least, native cloning is not the way to go. According to JSPerf, performing native cloning by creating a new function is nearly 800x slower than using JSON.stringify which is incredibly fast all the way across the board. Update for ES6 If you are using Javascript ES6 try this native method for cloning or shallow copy. Object.assign({}, obj);
Structured Cloning 2022 update: The structuredClone global function is already available in Firefox 94, Node 17 and Deno 1.14 The HTML standard includes an internal structured cloning/serialization algorithm that can create deep clones of objects. It is still limited to certain built-in types, but in addition to the few types supported by JSON it also supports Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays, and probably more in the future. It also preserves references within the cloned data, allowing it to support cyclical and recursive structures that would cause errors for JSON. Support in Node.js: The structuredClone global function is provided by Node 17.0: const clone = structuredClone(original); Previous versions: The v8 module in Node.js (as of Node 11) exposes the structured serialization API directly, but this functionality is still marked as "experimental", and subject to change or removal in future versions. If you're using a compatible version, cloning an object is as simple as: const v8 = require('v8'); const structuredClone = obj => { return v8.deserialize(v8.serialize(obj)); }; Direct Support in Browsers: Available in Firefox 94 The structuredClone global function will soon be provided by all major browsers (having previously been discussed in whatwg/html#793 on GitHub). It looks / will look like this: const clone = structuredClone(original); Until this is shipped, browsers' structured clone implementations are only exposed indirectly. Asynchronous Workaround: Usable. đ The lower-overhead way to create a structured clone with existing APIs is to post the data through one port of a MessageChannels. The other port will emit a message event with a structured clone of the attached .data. Unfortunately, listening for these events is necessarily asynchronous, and the synchronous alternatives are less practical. class StructuredCloner { constructor() { this.pendingClones_ = new Map(); this.nextKey_ = 0; const channel = new MessageChannel(); this.inPort_ = channel.port1; this.outPort_ = channel.port2; this.outPort_.onmessage = ({data: {key, value}}) => { const resolve = this.pendingClones_.get(key); resolve(value); this.pendingClones_.delete(key); }; this.outPort_.start(); } cloneAsync(value) { return new Promise(resolve => { const key = this.nextKey_++; this.pendingClones_.set(key, resolve); this.inPort_.postMessage({key, value}); }); } } const structuredCloneAsync = window.structuredCloneAsync = StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner); Example Use: const main = async () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = await structuredCloneAsync(original); // They're different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // They're cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // They contain equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; main(); Synchronous Workarounds: Awful! 𤢠There are no good options for creating structured clones synchronously. Here are a couple of impractical hacks instead. history.pushState() and history.replaceState() both create a structured clone of their first argument, and assign that value to history.state. You can use this to create a structured clone of any object like this: const structuredClone = obj => { const oldState = history.state; history.replaceState(obj, null); const clonedObj = history.state; history.replaceState(oldState, null); return clonedObj; }; Example Use: 'use strict'; const main = () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = structuredClone(original); // They're different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // They're cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // They contain equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; const structuredClone = obj => { const oldState = history.state; history.replaceState(obj, null); const clonedObj = history.state; history.replaceState(oldState, null); return clonedObj; }; main(); Though synchronous, this can be extremely slow. It incurs all of the overhead associated with manipulating the browser history. Calling this method repeatedly can cause Chrome to become temporarily unresponsive. The Notification constructor creates a structured clone of its associated data. It also attempts to display a browser notification to the user, but this will silently fail unless you have requested notification permission. In case you have the permission for other purposes, we'll immediately close the notification we've created. const structuredClone = obj => { const n = new Notification('', {data: obj, silent: true}); n.onshow = n.close.bind(n); return n.data; }; Example Use: 'use strict'; const main = () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = structuredClone(original); // They're different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // They're cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // They contain equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; const structuredClone = obj => { const n = new Notification('', {data: obj, silent: true}); n.close(); return n.data; }; main();
Assuming that you have only properties and not any functions in your object, you can just use: var newObject = JSON.parse(JSON.stringify(oldObject));
If there wasn't any builtin one, you could try: function clone(obj) { if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj) return obj; if (obj instanceof Date) var temp = new obj.constructor(); //or new Date(obj); else var temp = obj.constructor(); for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { obj['isActiveClone'] = null; temp[key] = clone(obj[key]); delete obj['isActiveClone']; } } return temp; }
The efficient way to clone(not deep-clone) an object in one line of code An Object.assign method is part of the ECMAScript 2015 (ES6) standard and does exactly what you need. var clone = Object.assign({}, obj); The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. Read more... The polyfill to support older browsers: if (!Object.assign) { Object.defineProperty(Object, 'assign', { enumerable: false, configurable: true, writable: true, value: function(target) { 'use strict'; if (target === undefined || target === null) { throw new TypeError('Cannot convert first argument to object'); } var to = Object(target); for (var i = 1; i < arguments.length; i++) { var nextSource = arguments[i]; if (nextSource === undefined || nextSource === null) { continue; } nextSource = Object(nextSource); var keysArray = Object.keys(nextSource); for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { var nextKey = keysArray[nextIndex]; var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); if (desc !== undefined && desc.enumerable) { to[nextKey] = nextSource[nextKey]; } } } return to; } }); }
Deep copy by performance: Ranked from best to worst spread operator ... (primitive arrays - only) splice(0) (primitive arrays - only) slice() (primitive arrays - only) concat() (primitive arrays - only) custom function, as seen below (any array) jQuery's $.extend() (any array) JSON.parse(JSON.stringify()) (primitive and literal arrays - only) Underscore's _.clone() (primitive and literal arrays - only) Lodash's _.cloneDeep() (any array) Where: primitives = strings, numbers, and booleans literals = object literals {}, array literals [] any = primitives, literals, and prototypes Deep copy an array of primitives: let arr1a = [1, 'a', true]; To deep copy arrays with primitives only (i.e. numbers, strings, and booleans), reassignment, slice(), concat(), and Underscore's clone() can be used. Where spread has the fastest performance: let arr1b = [...arr1a]; And where slice() has better performance than concat(): https://jsbench.me/x5ktn7o94d/ let arr1c = arr1a.splice(0); let arr1d = arr1a.slice(); let arr1e = arr1a.concat(); Deep copy an array of primitive and object literals: let arr2a = [1, 'a', true, {}, []]; let arr2b = JSON.parse(JSON.stringify(arr2a)); Deep copy an array of primitive, object literals, and prototypes: let arr3a = [1, 'a', true, {}, [], new Object()]; Write a custom function (has faster performance than $.extend() or JSON.parse): 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; } let arr3b = copy(arr3a); Or use third-party utility functions: let arr3c = $.extend(true, [], arr3a); // jQuery Extend let arr3d = _.cloneDeep(arr3a); // Lodash Note: jQuery's $.extend also has better performance than JSON.parse(JSON.stringify()): js-deep-copy jquery-extend-vs-json-parse
This is what I'm using: function cloneObject(obj) { var clone = {}; for(var i in obj) { if(typeof(obj[i])=="object" && obj[i] != null) clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; }
Code: // extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned function extend(from, to) { if (from == null || typeof from != "object") return from; if (from.constructor != Object && from.constructor != Array) return from; if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function || from.constructor == String || from.constructor == Number || from.constructor == Boolean) return new from.constructor(from); to = to || new from.constructor(); for (var name in from) { to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name]; } return to; } Test: var obj = { date: new Date(), func: function(q) { return 1 + q; }, num: 123, text: "asdasd", array: [1, "asd"], regex: new RegExp(/aaa/i), subobj: { num: 234, text: "asdsaD" } } var clone = extend(obj);
Deep copying objects in JavaScript (I think the best and the simplest) 1. Using JSON.parse(JSON.stringify(object)); var obj = { a: 1, b: { c: 2 } } var newObj = JSON.parse(JSON.stringify(obj)); obj.b.c = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 2.Using created method function cloneObject(obj) { var clone = {}; for(var i in obj) { if(obj[i] != null && typeof(obj[i])=="object") clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; } var obj = { a: 1, b: { c: 2 } } var newObj = cloneObject(obj); obj.b.c = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 3. Using Lo-Dash's _.cloneDeep link lodash var obj = { a: 1, b: { c: 2 } } var newObj = _.cloneDeep(obj); obj.b.c = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 4. Using Object.assign() method var obj = { a: 1, b: 2 } var newObj = _.clone(obj); obj.b = 20; console.log(obj); // { a: 1, b: 20 } console.log(newObj); // { a: 1, b: 2 } BUT WRONG WHEN var obj = { a: 1, b: { c: 2 } } var newObj = Object.assign({}, obj); obj.b.c = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG // Note: Properties on the prototype chain and non-enumerable properties cannot be copied. 5.Using Underscore.js _.clone link Underscore.js var obj = { a: 1, b: 2 } var newObj = _.clone(obj); obj.b = 20; console.log(obj); // { a: 1, b: 20 } console.log(newObj); // { a: 1, b: 2 } BUT WRONG WHEN var obj = { a: 1, b: { c: 2 } } var newObj = _.cloneDeep(obj); obj.b.c = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG // (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.) JSBEN.CH Performance Benchmarking Playground 1~3 http://jsben.ch/KVQLd
Cloning an object was always a concern in JS, but it was all about before ES6, I list different ways of copying an object in JavaScript below, imagine you have the Object below and would like to have a deep copy of that: var obj = {a:1, b:2, c:3, d:4}; There are few ways to copy this object, without changing the origin: ES5+, Using a simple function to do the copy for you: function deepCopyObj(obj) { if (null == obj || "object" != typeof obj) return obj; if (obj instanceof Date) { var copy = new Date(); copy.setTime(obj.getTime()); return copy; } if (obj instanceof Array) { var copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = deepCopyObj(obj[i]); } return copy; } if (obj instanceof Object) { var copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = deepCopyObj(obj[attr]); } return copy; } throw new Error("Unable to copy obj this object."); } ES5+, using JSON.parse and JSON.stringify. var deepCopyObj = JSON.parse(JSON.stringify(obj)); Angular: var deepCopyObj = angular.copy(obj); jQuery: var deepCopyObj = jQuery.extend(true, {}, obj); Underscore.js & Lodash: var deepCopyObj = _.cloneDeep(obj); //latest version of Underscore.js makes shallow copy Hope these helpâŚ
var clone = function() { var newObj = (this instanceof Array) ? [] : {}; for (var i in this) { if (this[i] && typeof this[i] == "object") { newObj[i] = this[i].clone(); } else { newObj[i] = this[i]; } } return newObj; }; Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});
Thereâs a library (called âcloneâ), that does this quite well. It provides the most complete recursive cloning/copying of arbitrary objects that I know of. It also supports circular references, which is not covered by the other answers, yet. You can find it on npm, too. It can be used for the browser as well as Node.js. Here is an example on how to use it: Install it with npm install clone or package it with Ender. ender build clone [...] You can also download the source code manually. Then you can use it in your source code. var clone = require('clone'); var a = { foo: { bar: 'baz' } }; // inital value of a var b = clone(a); // clone a -> b a.foo.bar = 'foo'; // change a console.log(a); // { foo: { bar: 'foo' } } console.log(b); // { foo: { bar: 'baz' } } (Disclaimer: Iâm the author of the library.)
I know this is an old post, but I thought this may be of some help to the next person who stumbles along. As long as you don't assign an object to anything it maintains no reference in memory. So to make an object that you want to share among other objects, you'll have to create a factory like so: var a = function(){ return { father:'zacharias' }; }, b = a(), c = a(); c.father = 'johndoe'; alert(b.father);
If you're using it, the Underscore.js library has a clone method. var newObject = _.clone(oldObject);
Here's a version of ConroyP's answer above that works even if the constructor has required parameters: //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that's all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; } function deepCopy(obj) { if(obj == null || typeof(obj) !== 'object'){ return obj; } //make sure the returned object has the same prototype as the original var ret = object_create(obj.constructor.prototype); for(var key in obj){ ret[key] = deepCopy(obj[key]); } return ret; } This function is also available in my simpleoo library. Edit: Here's a more robust version (thanks to Justin McCandless this now supports cyclic references as well): /** * Deep copy an object (make copies of all its object properties, sub-properties, etc.) * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone * that doesn't break if the constructor has required parameters * * It also borrows some code from http://stackoverflow.com/a/11621004/560114 */ function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) { if(src === null || typeof(src) !== 'object'){ return src; } //Honor native/custom clone methods if(typeof src.clone == 'function'){ return src.clone(true); } //Special cases: //Date if(src instanceof Date){ return new Date(src.getTime()); } //RegExp if(src instanceof RegExp){ return new RegExp(src); } //DOM Element if(src.nodeType && typeof src.cloneNode == 'function'){ return src.cloneNode(true); } // Initialize the visited objects arrays if needed. // This is used to detect cyclic references. if (_visited === undefined){ _visited = []; _copiesVisited = []; } // Check if this object has already been visited var i, len = _visited.length; for (i = 0; i < len; i++) { // If so, get the copy we already made if (src === _visited[i]) { return _copiesVisited[i]; } } //Array if (Object.prototype.toString.call(src) == '[object Array]') { //[].slice() by itself would soft clone var ret = src.slice(); //add it to the visited array _visited.push(src); _copiesVisited.push(ret); var i = ret.length; while (i--) { ret[i] = deepCopy(ret[i], _visited, _copiesVisited); } return ret; } //If we've reached here, we have a regular object //make sure the returned object has the same prototype as the original var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__); if (!proto) { proto = src.constructor.prototype; //this line would probably only be reached by very old browsers } var dest = object_create(proto); //add this object to the visited array _visited.push(src); _copiesVisited.push(dest); for (var key in src) { //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc. //For an example of how this could be modified to do so, see the singleMixin() function dest[key] = deepCopy(src[key], _visited, _copiesVisited); } return dest; } //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that's all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; }
The following creates two instances of the same object. I found it and am using it currently. It's simple and easy to use. var objToCreate = JSON.parse(JSON.stringify(cloneThis));
Crockford suggests (and I prefer) using this function: function object(o) { function F() {} F.prototype = o; return new F(); } var newObject = object(oldObject); It's terse, works as expected and you don't need a library. EDIT: This is a polyfill for Object.create, so you also can use this. var newObject = Object.create(oldObject); NOTE: If you use some of this, you may have problems with some iteration who use hasOwnProperty. Because, create create new empty object who inherits oldObject. But it is still useful and practical for cloning objects. For exemple if oldObject.a = 5; newObject.a; // is 5 but: oldObject.hasOwnProperty(a); // is true newObject.hasOwnProperty(a); // is false
function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; }
Lodash has a nice _.cloneDeep(value) method: var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false
Shallow copy one-liner (ECMAScript 5th edition): var origin = { foo : {} }; var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{}); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true And shallow copy one-liner (ECMAScript 6th edition, 2015): var origin = { foo : {} }; var copy = Object.assign({}, origin); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true
There seems to be no ideal deep clone operator yet for array-like objects. As the code below illustrates, John Resig's jQuery cloner turns arrays with non-numeric properties into objects that are not arrays, and RegDwight's JSON cloner drops the non-numeric properties. The following tests illustrate these points on multiple browsers: function jQueryClone(obj) { return jQuery.extend(true, {}, obj) } function JSONClone(obj) { return JSON.parse(JSON.stringify(obj)) } var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]]; arrayLikeObj.names = ["m", "n", "o"]; var JSONCopy = JSONClone(arrayLikeObj); var jQueryCopy = jQueryClone(arrayLikeObj); alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) + "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) + "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names + "\nAnd what are the JSONClone names? " + JSONCopy.names)
Just because I didn't see AngularJS mentioned and thought that people might want to know... angular.copy also provides a method of deep copying objects and arrays.
I have two good answers depending on whether your objective is to clone a "plain old JavaScript object" or not. Let's also assume that your intention is to create a complete clone with no prototype references back to the source object. If you're not interested in a complete clone, then you can use many of the Object.clone() routines provided in some of the other answers (Crockford's pattern). For plain old JavaScript objects, a tried and true good way to clone an object in modern runtimes is quite simply: var clone = JSON.parse(JSON.stringify(obj)); Note that the source object must be a pure JSON object. This is to say, all of its nested properties must be scalars (like boolean, string, array, object, etc). Any functions or special objects like RegExp or Date will not be cloned. Is it efficient? Heck yes. We've tried all kinds of cloning methods and this works best. I'm sure some ninja could conjure up a faster method. But I suspect we're talking about marginal gains. This approach is just simple and easy to implement. Wrap it into a convenience function and if you really need to squeeze out some gain, go for at a later time. Now, for non-plain JavaScript objects, there isn't a really simple answer. In fact, there can't be because of the dynamic nature of JavaScript functions and inner object state. Deep cloning a JSON structure with functions inside requires you recreate those functions and their inner context. And JavaScript simply doesn't have a standardized way of doing that. The correct way to do this, once again, is via a convenience method that you declare and reuse within your code. The convenience method can be endowed with some understanding of your own objects so you can make sure to properly recreate the graph within the new object. We're written our own, but the best general approach I've seen is covered here: http://davidwalsh.name/javascript-clone This is the right idea. The author (David Walsh) has commented out the cloning of generalized functions. This is something you might choose to do, depending on your use case. The main idea is that you need to special handle the instantiation of your functions (or prototypal classes, so to speak) on a per-type basis. Here, he's provided a few examples for RegExp and Date. Not only is this code brief, but it's also very readable. It's pretty easy to extend. Is this efficient? Heck yes. Given that the goal is to produce a true deep-copy clone, then you're going to have to walk the members of the source object graph. With this approach, you can tweak exactly which child members to treat and how to manually handle custom types. So there you go. Two approaches. Both are efficient in my view.
I am late to answer this question, but I have an another way of cloning the object: function cloneObject(obj) { if (obj === null || typeof(obj) !== 'object') return obj; var temp = obj.constructor(); // changed for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { obj['isActiveClone'] = null; temp[key] = cloneObject(obj[key]); delete obj['isActiveClone']; } } return temp; } var b = cloneObject({"a":1,"b":2}); // calling which is much better and faster then: var a = {"a":1,"b":2}; var b = JSON.parse(JSON.stringify(a)); and var a = {"a":1,"b":2}; // Deep copy var newObject = jQuery.extend(true, {}, a); I have bench-marked the code and you can test the results here: and sharing the results: References: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
Only when you can use ECMAScript 6 or transpilers. Features: Won't trigger getter/setter while copying. Preserves getter/setter. Preserves prototype informations. Works with both object-literal and functional OO writing styles. Code: function clone(target, source){ for(let key in source){ // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter. let descriptor = Object.getOwnPropertyDescriptor(source, key); if(descriptor.value instanceof String){ target[key] = new String(descriptor.value); } else if(descriptor.value instanceof Array){ target[key] = clone([], descriptor.value); } else if(descriptor.value instanceof Object){ let prototype = Reflect.getPrototypeOf(descriptor.value); let cloneObject = clone({}, descriptor.value); Reflect.setPrototypeOf(cloneObject, prototype); target[key] = cloneObject; } else { Object.defineProperty(target, key, descriptor); } } let prototype = Reflect.getPrototypeOf(source); Reflect.setPrototypeOf(target, prototype); return target; }
This isn't generally the most efficient solution, but it does what I need. Simple test cases below... function clone(obj, clones) { // Makes a deep copy of 'obj'. Handles cyclic structures by // tracking cloned obj's in the 'clones' parameter. Functions // are included, but not cloned. Functions members are cloned. var new_obj, already_cloned, t = typeof obj, i = 0, l, pair; clones = clones || []; if (obj === null) { return obj; } if (t === "object" || t === "function") { // check to see if we've already cloned obj for (i = 0, l = clones.length; i < l; i++) { pair = clones[i]; if (pair[0] === obj) { already_cloned = pair[1]; break; } } if (already_cloned) { return already_cloned; } else { if (t === "object") { // create new object new_obj = new obj.constructor(); } else { // Just use functions as is new_obj = obj; } clones.push([obj, new_obj]); // keep track of objects we've cloned for (key in obj) { // clone object members if (obj.hasOwnProperty(key)) { new_obj[key] = clone(obj[key], clones); } } } } return new_obj || obj; } Cyclic array test... a = [] a.push("b", "c", a) aa = clone(a) aa === a //=> false aa[2] === a //=> false aa[2] === a[2] //=> false aa[2] === aa //=> true Function test... f = new Function f.a = a ff = clone(f) ff === f //=> true ff.a === a //=> false
For the people who want to use the JSON.parse(JSON.stringify(obj)) version, but without losing the Date objects, you can use the second argument of parse method to convert the strings back to Date: function clone(obj) { var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; return JSON.parse(JSON.stringify(obj), function(k, v) { if (typeof v === 'string' && regExp.test(v)) return new Date(v) return v; }) } // usage: var original = { a: [1, null, undefined, 0, {a:null}, new Date()], b: { c(){ return 0 } } } var cloned = clone(original) console.log(cloned)
I disagree with the answer with the greatest votes here. A Recursive Deep Clone is much faster than the JSON.parse(JSON.stringify(obj)) approach mentioned. Jsperf ranks it number one here: https://jsperf.com/deep-copy-vs-json-stringify-json-parse/5 Jsben from the answer above updated to show that a recursive deep clone beats all the others mentioned: http://jsben.ch/13YKQ And here's the function for quick reference: function cloneDeep (o) { let newO let i if (typeof o !== 'object') return o if (!o) return o if (Object.prototype.toString.apply(o) === '[object Array]') { newO = [] for (i = 0; i < o.length; i += 1) { newO[i] = cloneDeep(o[i]) } return newO } newO = {} for (i in o) { if (o.hasOwnProperty(i)) { newO[i] = cloneDeep(o[i]) } } return newO }
Here is a comprehensive clone() method that can clone any JavaScript object. It handles almost all the cases: function clone(src, deep) { var toString = Object.prototype.toString; if (!src && typeof src != "object") { // Any non-object (Boolean, String, Number), null, undefined, NaN return src; } // Honor native/custom clone methods if (src.clone && toString.call(src.clone) == "[object Function]") { return src.clone(deep); } // DOM elements if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") { return src.cloneNode(deep); } // Date if (toString.call(src) == "[object Date]") { return new Date(src.getTime()); } // RegExp if (toString.call(src) == "[object RegExp]") { return new RegExp(src); } // Function if (toString.call(src) == "[object Function]") { //Wrap in another method to make sure == is not true; //Note: Huge performance issue due to closures, comment this :) return (function(){ src.apply(this, arguments); }); } var ret, index; //Array if (toString.call(src) == "[object Array]") { //[].slice(0) would soft clone ret = src.slice(); if (deep) { index = ret.length; while (index--) { ret[index] = clone(ret[index], true); } } } //Object else { ret = src.constructor ? new src.constructor() : {}; for (var prop in src) { ret[prop] = deep ? clone(src[prop], true) : src[prop]; } } return ret; };
Is there a way to wait until a function is finished in React Native?
I'm trying to get information (true/false) from AsyncStorage in a function and create a string which is importent to fetch data in the next step. My problem is, the function is not finished until the string is required. I tried many solutions from the internet like async function and await getItem or .done() or .then(), but none worked out for me. //_getFetchData() AsyncStorage.getAllKeys().then((result) => { //get all stored Keys valuelength = result.length; if (valuelength !== 0) { for (let i = 0; i < valuelength; i++) { if (result[i].includes("not") == false) { //get Keys without not AsyncStorage.getItem(result[i]).then((resultvalue) => { if (resultvalue === 'true') { if (this.state.firstValue) { this.state.channels = this.state.channels + "channel_id" + result[i]; console.log("channel: " + this.state.channels); } else { this.state.channels = this.state.channels + "channel" + result[i]; } } }); } return this.state.channels; _fetchData() { var channel = this._getFetchData(); console.log("channel required: " + channel); } The current behaviour is that the console displays first "channel required: " than "channel: channel_id0".
Aspects in your question are unclear: You don't say when this.state.firstValue is set, and how that relates to what you are trying to accomplish. You have a for-loop where you could be setting the same value multiple times. You mutate the state rather than set it. This is not good, see this SO question for more on that. There are somethings we can do to make your code easier to understand. Below I will show a possible refactor. Explaining what I am doing at each step. I am using async/await because it can lead to much tidier and easier to read code, rather than using promises where you can get lost in callbacks. Get all the keys from AsyncStorage Make sure that there is a value for all the keys. Filter the keys so that we only include the ones that do not contain the string 'not'. Use a Promise.all, this part is important as it basically gets all the values for each of the keys that we just found and puts them into an array called items Each object in the items array has a key and a value property. We then filter the items so that only the ones with a item.value === 'true' remain. We then filter the items so that only the ones with a item.value !== 'true' remain. (this may be optional it is really dependent on what you want to do) What do we return? You need to add that part. Here is the refactor: _getFetchData = async () => { let allKeys = await AsyncStorage.getAllKeys(); // 1 if (allKeys.length) { // 2 let filteredKeys = allKeys.filter(key => !key.includes('not')); // 3 let items = await Promise.all(filteredKeys.map(async key => { // 4 let value = await AsyncStorage.getItem(key); return { key, value }; // 5 })) let filteredTrueItems = items.filter(item => items.value === 'true'); // 6 let filteredFalseItems = items.filter(item => items.value !== 'true'); // 7 // now you have two arrays one with the items that have the true values // and one with the items that have the false values // at this points you can decide what to return as it is not // that clear from your question // return the value that your want // 8 } else { // return your default value if there are no keys // 8 } } You would call this function as follows: _fetchData = async () => { let channel = await this._getFetchData(); console.log("channel required: " + channel); } Although the above will work, it will not currently return a value as you haven't made it clear which value you wish to return. I would suggest you build upon the code that I have written here and update it so that it returns the values that you want. Further reading For further reading I would suggest these awesome articles by Michael Chan that discuss state https://medium.learnreact.com/setstate-is-asynchronous-52ead919a3f0 https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296 https://medium.learnreact.com/setstate-takes-a-function-56eb940f84b6 I would also suggest taking some time to read up about async/await and promises https://medium.com/#bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8 And finally this article and SO question on Promise.all are quite good https://www.taniarascia.com/promise-all-with-async-await/ Using async/await with a forEach loop
Try this instead. Async functions and Promises can be tricky to get right and can be difficult to debug but you're on the right track. async _getFetchData() { let channels = ""; let results = await AsyncStorage.getAllKeys(); results.forEach((result) => { if (result.includes("not") === false) { let item = await AsyncStorage.getItem(result); if (item === 'true') { console.log(`channel: ${result}`) channels = `channel_id ${result}`; } } }); return channels; } _fetchData() { this._getFetchData().then((channels) => { console.log(`channel required: ${channel}`); }); }
what if you wrap the _getFetchData() in a Promise? This would enable you to use var channel = this._getFetchData().then(console.log("channel required: " + channel)); Otherwise the console.log won't wait for the execution of the _getFetchData(). This is what the console.log is telling you. it just logs the string. the variable is added after the async operation is done. UPDATE I would try this: //_getFetchData() AsyncStorage.getAllKeys().then((result) => { //get all stored Keys valuelength = result.length; if (valuelength !== 0) { for (let i = 0; i < valuelength; i++) { if (result[i].includes("not") == false) { //get Keys without not AsyncStorage.getItem(result[i]).then((resultvalue) => { if (resultvalue === 'true') { if (this.state.firstValue) { this.state.channels = this.state.channels + "channel_id" + result[i]; console.log("channel: " + this.state.channels); } else { this.state.channels = this.state.channels + "channel" + result[i]; } } }); } return new Promise((resolve, reject) => { this.state.channels !=== undefined ? resolve(this.state.channels) : reject(Error('error ')); } _fetchData() { var channel = this._getFetchData().then(console.log("channel required: " + channel)); } maybe you must change the this.state.channels !=== undefined to an expression that's matches the default value of this.state.channels.
React Native: how can I achieve the dynamic keys with multiple objects
Here is my code I tried, var array=[]; var list = this.state.list; var getList = function(i){ var add = +i + 1; return { ["value"+add]:{ Description:list[i].Description, Length:list[i].Length, Height:list[i].Height, Weight:list[i].Weight, VolumeWeight:list[i].VolumeWeight, ActualWeight:list[i].ActualWeight, } } }.bind(this) for(var i in list){ array.push(getList(i)); } var dataArray = array.map(function(e){ return JSON.stringify(e); }); dataString = dataArray.join(","); data1 = { ConsigneeBranchName:this.state.searchText, ConsigneeBranchCode:this.state.code, ConsigneeBranchFullAddress:this.state.DAddress, SenderBranchCode:this.state.code1, SenderBranchName:this.state.searchTexts, SenderBranchFullAddress:this.state.Address, CreatedByEmployeeCode:id, CreatedByEmployeeFullName:userName, jsonString:{ JsonValues:{ id:"MyID", values:dataString } } } But I want the result is exactly this var result = { "ConsigneeBranchName":"", "ConsigneeBranchCode":"", "ConsigneeBranchFullAddress":"", "SenderBranchCode":"", "SenderBranchName":"", "SenderBranchFullAddress":"", "CreatedByEmployeeCode":"", "CreatedByEmployeeFullName":"", "jsonString":"{ "JsonValues": { "id": "MyID", "values": { "value1":{ "Descriptionâ:"testSmarter1", "Lengthâ:"60", "Heightâ:"50", "Weightâ:"70", "VolumeWeightâ:"75", "ActualWeightâ:â78" }, "value2:{ "Description":"Documents", "Length":"120", "Height":"68", "Weight":"75", "VolumeWeight":"122.4", "ActualWeight":"123" }, } } } }; Please any one help me I want the object with dynamic keys within a single object {key1:{des:1,value:as},key2:{des:2,value:aw},key3:{des:3,value:au}} can you please help me I have tried so many times see this below image I want this part, inside the single object, I can join multiple objects with dynamic keys
lodash already has a function called keyBy, you can use it to get this functionality. If adding lodash doesn't make sense in your project. I have implemented a vanilla JS version. function keyBy(array, mapperFn) { const resultObj = {}; array.map(item => resultObj[mapperFn(item)] = item); return resultObj; } function arrayToObject (array, keyName = 'id') { return keyBy(array, function(element) {return element[keyName]}); } API: arrayToObject(targetArray, stringNameOfThePorpertyYouWantToUseAsKey); USAGE: const listOfUsers = [{name: 'Jenitha', reputation: 6}, {name: 'Chandan', reputation: 3}]; const mapOfUsersByName = arrayToObject(listOfUsers, 'name');
Extjs, define scope of column's renderer function
I think that, bydefault renderer always refer to its calling object as scope. and we do not have to define anything but in my grid i have different behaviour. I have to define rendere function runtime. I make column object and for each column i assign renderer. for (var i = 0; i < columns.length; i++) { newRenderer = function (v, m, r, rI, cI, s) { if(this.originalRend) <<<<<----------------- // "this" is not object of column but whole page. { //then call original} else { // call new renderer } } columns[i].originalRend = columns[i].renderer; columns[i].renderer = newRenderer; } In my newRenderer function "THIS" does not refer to column object. WHY????? and how to do that????
You should be able to use scope as a config option of the column, and that will be used by the renderer: { renderer: function(val){ return val.trim(); }, scope: this } Or, in the case of your code: for (var i = 0; i < columns.length; i++) { newRenderer = function (v, m, r, rI, cI, s) { if(this.originalRend) <<<<<----------------- // "this" is not object of column but whole page. { //then call original} else { // call new renderer } } //THIS LINE ADDED: columns[i].scope = columns[i]; columns[i].originalRend = columns[i].renderer; columns[i].renderer = newRenderer; } See docs here: http://docs.sencha.com/extjs/4.1.3/#!/api/Ext.grid.column.Column-cfg-scope
Scope does not work as column or this. The best way is to overrite prepareData method of header container. Ext.override(Ext.grid.header.Container, { prepareData: function(data, rowIdx, record, view, panel) { //console.log("we r in prepare Data"); var me = this, obj = {}, headers = me.gridDataColumns || me.getGridColumns(), headersLn = headers.length, dirtyCls = me.dirtyCls, colIdx = 0, header, headerId, renderer, value, metaData, store = panel.store; for (; colIdx < headersLn; colIdx++) { metaData = { tdCls: '', style: '' }; header = headers[colIdx]; headerId = header.id; renderer = header.renderer; value = data[header.dataIndex]; if (typeof renderer == "function") { value = renderer.call( //--------change made below.------------------ header.scope || header || me.ownerCt, //----------------end------------------------- value, // metadata per cell passing an obj by reference so that // it can be manipulated inside the renderer metaData, record, rowIdx, colIdx, store, view ); } // <debug> if (metaData.css) { // This warning attribute is used by the compat layer obj.cssWarning = true; metaData.tdCls = metaData.css; delete metaData.css; } // </debug> if (me.markDirty) { obj[headerId + '-modified'] = record.isModified(header.dataIndex) ? dirtyCls : ''; } obj[headerId+'-tdCls'] = metaData.tdCls; obj[headerId+'-tdAttr'] = metaData.tdAttr; obj[headerId+'-style'] = metaData.style; if (typeof value === 'undefined' || value === null || value === '') { value = header.emptyCellText; } obj[headerId] = value; } return obj; }, }
How do you serialise Ember objects?
I need to use localStorage to store some Ember objects. I notice that Ember objects have properties with names like __ember1334992182483. When I call JSON.stringify() on Ember objects, these __ember* properties are not serialised. Why is this? I'm not saying that I want to serialize those properties. I am just curious about what exactly they are and how they are implemented such that they are not serialised. I am using cycle.js (https://github.com/douglascrockford/JSON-js/blob/master/cycle.js) to encode my data structures that contain duplicate references into a string that can be used for reconstructing the original data structures. It lets you do this: a = {a:1} b = {b:1} c = [[a, b], [b, a]] foo = JSON.stringify(JSON.decycle(c)) // "[[{'a':1},{'b':1}],[{'$ref':'$[0][1]'},{'$ref':'$[0][0]'}]]" JSON.retrocycle(JSON.parse(foo)) // reconstruct c For Ember objects I can do the same thing, but I also need to pass the deserialised objects into Ember.Object.create() because they are deserialised as plain JavaScript objects. Is this the best way to serialise/deserialise Ember objects? Is there a recommended technique for this?
For serialization and deserialization you could do something along this lines, see http://jsfiddle.net/pangratz666/NVpng/: App.Serializable = Ember.Mixin.create({ serialize: function() { var propertyNames = this.get('propertyNames') || []; return this.getProperties(propertyNames); }, deserialize: function(hash) { this.setProperties(hash); } }); App.Person = Ember.Object.extend(App.Serializable, { propertyNames: 'firstName title fullName'.w(), fullName: function() { return '%# %#'.fmt(this.get('title'), this.get('firstName')); }.property('firstName', 'title') }); var hansi = App.Person.create({ firstName: 'Hansi', title: 'Mr.' }); // { firstName: 'hansi', title: 'Mr.', fullName: 'Mr. Hansi' } console.log( hansi.serialize() ); var hubert = App.Person.create(); hubert.deserialize({ firstName: 'Hubert', title: 'Mr.' }); console.log( hubert.serialize() );â UPDATE: Also have a look at the similar question Ember model to json
I would use ember-data and write a datastore adapter for this.
I have: fixed and simplified code added circular reference prevention added use of get of value removed all of the default properties of an empty component //Modified by Shimon Doodkin //Based on answers of: #leo, #pangratz, #kevin-pauli, #Klaus //http://stackoverflow.com/questions/8669340 App.Jsonable = Em.Mixin.create({ getJson : function (keysToSkip, visited) { //getJson() called with no arguments, // they are to pass on values during recursion. if (!keysToSkip) keysToSkip = Object.keys(Ember.Component.create()); if (!visited) visited = []; visited.push(this); var getIsFunction; var jsonValue = function (attr, key, obj) { if (Em.isArray(attr)) return attr.map(jsonValue); if (App.Jsonable.detect(attr)) return attr.getJson(keysToSkip, visited); return getIsFunction?obj.get(key):attr; }; var base; if (!Em.isNone(this.get('jsonProperties'))) base = this.getProperties(this.get('jsonProperties')); else base = this; getIsFunction=Em.typeOf(base.get) === 'function'; var json = {}; var hasProp = Object.prototype.hasOwnProperty; for (var key in base) { if (!hasProp.call(base, key) || keysToSkip.indexOf(key) != -1) continue; var value = base[key]; // there are usual circular references // on keys: ownerView, controller, context === base if ( value === base || value === 'toString' || Em.typeOf(value) === 'function') continue; // optional, works also without this, // the rule above if value === base covers the usual case if (visited.indexOf(value) != -1) continue; json[key] = jsonValue(value, key, base); } visited.pop(); return json; } }); /* example: DeliveryInfoInput = Ember.Object.extend(App.Jsonable,{ jsonProperties: ["title","value","name"], //Optionally specify properties for json title:"", value:"", input:false, textarea:false, size:22, rows:"", name:"", hint:"" }) */