js-object-clone
v0.4.2
Published
Deep cloning and comparison for ES5
Downloads
347
Readme
object-clone.js
Deep cloning and comparison for ES5
SYNOPSIS
// just for convenience
var log = function(){ console.log.apply(console, [].slice.call(arguments)) };
var src = { name: 'dankogai', lang: ['perl'] };
var dst = Object.clone(src); // shallow copy
log( Object.is(src, dst) ); // false
log( Object.equals(src, dst) ); // true
dst.lang.push('javascript');
log(JSON.stringify(src.lang) ); // ["perl","javascript"] because dst is shallow-copied
dst = Object.clone(src, true); // deep copy
dst.lang = dst.lang.reverse();
log( JSON.stringify(src.lang) ); // ["perl","javascript"]
log( JSON.stringify(dst.lang) ); // ["javascript","perl"]
REQUIREMENT
EcmaScript 5 compliance.
DESCRIPTION
This script installs following functions to Object
.
If the follwing ES6 functions are missing they are polyfilled.
Object.is()
Object.isnt()
cf. http://wiki.ecmascript.org/doku.php?id=harmony:egal
Object.clone( obj , deep, spec )
Clones the object obj. When deep is true
, it attempts to deep
clone obj.
For spec, see below.
Unlike many other implementations of object cloners, This one:
- can deep clone upon request
- copies the ES5 descriptor of every property that
Object.getOwnPropertyDescriptor()
returns - copies the restriction of the object that the following functions cast upon:
Object.preventExtensions()
Object.seal()
Object.freeze()
Custom Objects
You can clone custom objects so long as its constructor is written in JavaScript:
var Point = function(x, y) {
if (!(this instanceof Point)) return new Point(x, y);
this.x = x*1;
this.y = y*1;
};
Point.prototype = {
distance: function(pt) {
if (!pt) pt = Point(0,0);
var dx = this.x - pt.x;
var dy = this.y - pt.y;
return Math.sqrt(dx*dx + dy*dy);
}
};
var src = Point(3,4);
var dst = Object.clone(src, true);
log( src === dst ); // false
log( Object.equals(src, dst) ); // true
log( dst.distance(Point(0,0)) ); // 5
If the type of obj is unsupported, it throws TypeError
:
dst = Object.clone(new Error); // [object Error] unsupported
Why DOM Elements are not supported
Note DOM Elements are not supported. It already has .cloneNode
so use it.
cf. https://developer.mozilla.org/en-US/docs/DOM/Node.cloneNode
It is rather trivial to add support for that since all you have to do
is delegate it to obj.cloneNode( deep ) (as a matter of fact my
early code did support that). But the author decided to drop that
since uneval()
of Firefox does not support that.
Object.equals( objX, objY, spec )
Compares the value of each property in objX and objY and returns
true
iff all properties are equal, otherwise false
.
Like Object.clone()
, Object.equals()
:
- compares ES5 descriptor
- compares restriction
Minute controls via spec
Version 0.3.0 introduced the third argument to Object.clone()
and
Object.equals()
which enables more minute control on how objects
are compared or cloned. It is a object with following default.
{
descriptors: true,
extensibility: true,
enumerator: Object.getOwnPropertyNames
}
.descriptor
If false
, descriptor specs are ignored except for value
.
src = {};
defineProperty(src, 0, {value:1});
dst = Object.clone(src, true, {descriptor:false} );
log(Object.equals(dst, src) ); // false;
log(Object.equals(dst, src, {descriptor:false})); // true;
.extensibility
If false
, extensibility is ignored
src = {};
Object.freeze(src);
dst = Object.clone(src, true, {extensibility:false});
log(Object.equals(dst, src) ); // false;
log(Object.equals(dst, src, {extensibility:true})); // true;
.enumerator
Set the function used to enumurate object. Change it to Object.keys
and all non-enumerable properties are ignored.
The example below emulates _.clone
and _.isEqual()
:
var spec = {
descriptors: false,
extensibility: false,
enumerator: Object.keys
};
if (!_) _ = {};
_.clone = function(src) { return Object.clone(src, false, spec) };
_.isEqual = function(x, y){ return Object.equals(x, y, spec) };
.filter
You can even set filter like this:
var ignore__ = function(desc, key, obj) {
return !key.match(/^__/);
};
src = {0:1, __id__:'src'};
dst = Object.clone(src, true, {filter:ignore__});
log(Object.equals(dst, src) ); // false;
log(Object.equals(dst, src, {filter:ignore__})); // true;
Like [].filter
, the call back function take three arguments
desc
The descriptor of the value. Note it is not the value itselfkey
The key. For most cases you need only this.
Should I make this the first argument? This is more consistent with array iterators, though.obj
The whole object
Circular Reference Support
As of 0.2.0, Object.clone()
and Object.equals()
handle
circular references iff ES6 WeakMap
is supported. As of this writing,
the following JS engines suppor that.
- node.js with
--harmony
- Chrome with Experimental JavaScript enabled via
chrome://flags/
. - FireFox since 6.0
cf. https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/WeakMap
BUGS
Like JSON
, Object.clone()
and Object.equals()
cannot handle
circular references unless WeakMap is supported.
It is not impossible to handle circular references in JavaScript since
you can check if the objects are identical via ===
operator. Yet it
is very impractical without object ID like object_id of Ruby or
refaddr
of Perl. Without object ID you have to linear search just
to check if the object is already visited. As a matter of fact the
reference implementation of Map and Set of ES6 resorts to linear
search.
With ES5 you can add hidden, immutable properties like .__id__
via
Object.defineProperty
but mutating objects for that is rude if not
unforgivable.
SEE ALSO
eval(uneval(obj))
Available only on firefox. Handles circular references.
_.clone() and _.cloneDeep()
Lacks deep cloning support and ES5 support. One of the reason why I resorted to writing this.
Lo-dash has _.cloneDeep() yet still lacks ES5 suppport.
- http://underscorejs.org/#clone
- http://lodash.com/docs#cloneDeep
The structured clone algorithm
Roughly the same but Blob, File and other user-agent specific objects are not yet supported. Unfortunately it is used only internally to exchange data with WebWorkers.
- https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm
- http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data