In certain programming languages, some values are said to be "immutable," meaning that once assigned to a variable name, they cannot be changed in part.
var s = 'string';
s += ' bean';
s
will still be 'string' even though we tried to alter it with the concatenation operator into 'string bean'.
That applies to ALL values. Unless you save the result of the operation into another variable name, the result does not persist.
With objects (wrapped natives like String, Object, Array, and Date), however, the object acts as a collection of methods that operate on an internal state (shorthand term meaning collection of values).
An array's state can be modified by several methods, such as reverse
, sort
, push
, pop
, shift
, splice
, etc.
var a = [3, 2, 1];
a.reverse();
a
will now render as [1, 2, 3]
.
Other array methods, such as concat
and slice
, return a new Array. Unless you save the result of concat
and slice
, the result does not persist.
Sometimes we want that, sometimes we don't.
In general, but not always, it is advisable to pass values to functions and work on copies of these values so that we don't introduce side-effects by accident.
JavaScript does not have a one-stop cloning method or syntax for objects of any type. (Hold your objections about JSON.stringify and JSON.parse for a moment.) For objects
and arrays
you can use the spread syntax to create new objects, {...object}
, or arrays, [...array]
whereas other objects have their own internal state that cannot be cloned via the spread syntax. For that reason, I avoid the spread syntax and use type-specific methods.
var o = { name: 'test' }
var a = ['2', 3, 'a', 1, 0];
var d = new Date('2019/2/1');
function obj(o) {
return Object.assign({}, o);
}
function arr(a) {
return a.slice();
}
function date(d) {
return new Date(typeof d.toISOString == 'function' ? d.toISOString() : d);
}
console.log(obj(o));
// {name: "test"}
console.log(obj(o) !== o);
// true
console.log(arr(a));
// ["2", 3, "a", 1, 0]
console.log(arr(a) !== a);
// true
console.log(date(d));
// Fri Feb 01 2019 00:00:00 GMT-0800 (Pacific Standard Time)
console.log(date(d) !== d);
// true
// Note that as Date
objects can be tricky, it is better to create date strings instead (they're immutable!) and construct Date
objects from those once you really need them.
var now = Date.now()
var anHourAgo = now - (60 * 60 * 1000); // minutes, seconds, milliseconds
console.log(new Date(now));
// Thu Feb 06 2020 17:19:10 GMT-0800 (Pacific Standard Time)
console.log(new Date(anHourAgo));
// Thu Feb 06 2020 16:19:10 GMT-0800 (Pacific Standard Time)
console.log(date(anHourAgo) !== date(anHourAgo));
// true
First let's create a test element:
var e = document.createElement('div');
e.textContent = 'test element';
e.setAttribute('id', 'hello')
document.body.appendChild(e);
Now we'll find it by selector and and make a deep copy of it:
function clone(selector) {
var clone = document.querySelector(selector);
return clone
// If it exists, use the cloneNode(deep) method;
? clone.cloneNode(true)
// if not, return an error-like message.
: { message: `no matching element for selector "${ selector }"` };
}
var a = clone('#hello');
var b = clone('#hello');
console.log( a !== e );
console.log( a !== b );
console.log( b !== e );
// should all print true
Some have suggested that an object clone method can be composed of the JSON methods, as follows:
function clone(object) {
return JSON.parse(JSON.stringify(source));
}
var result = clone({
key: undefined,
send: function () { return "send method called" }
});
console.log(result);
However, JSON is not an object type but a data format. The result of the above operation is to return an object without a key
field (because the value is undefined), or a send
method (because methods/functions are not allowed). An array with a custom property (e.g., array.hello = "hello"
) will be converted to an array without that property.
For more details, see this article by Petro Zubar (2019) → https://medium.com/@pmzubar/why-json-parse-json-stringify-is-a-bad-practice-to-clone-an-object-in-javascript-b28ac5e36521
Maybe.