
object-clone.js
Object.clone() with deep cloning support
SYNOPSIS
var log = function(){ console.log.apply(console, [].slice.call(arguments)) };
var src = { name: 'dankogai', lang: ['perl'] };
var dst = Object.clone(src);
log(Object.is(src, dst));
log(Object.equals(src, dst));
dst.lang.push('javascript');
log(JSON.stringify(dst.lang));
dst = Object.clone(src, true);
dst.lang = dst.lang.reverse();
log(JSON.stringify(src.lang));
log(JSON.stringify(dst.lang));
REQUIREMENT
EcmaScript 5 compliance.
DESCRIPTION
This script installs following functions to Object
Object.clone( obj , deep )
Clones the object obj. When deep is true, it attempts to deep clone obj.
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()
You can copy 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(Object.equals(src, dst));
log(dst.distance(Point(0,0));
If the type of obj is unsupported, it throws TypeError:
dst = Object.clone(new Error);
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 )
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
Object.is() and Object.isnt()
The following ES6 functions are also defined unless predefined (like Chrome 25):
- Object.is()
- Object.isnt()
See http://wiki.ecmascript.org/doku.php?id=harmony:egal for details.
BUGS
Like JSON, Object.clone() and Object.equals() cannot handle circular references.
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.