public
Last active

  • Download Gist
cloner.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
(function(context, attachPrototype) {
// obj: Object or scalar to clone. Accepts any value or lack thereof. Technically optional.
// deep: Whether to perform a deep clone. Defaults to false (shallow clone). Optional.
// deepFunctions: Whether to clone functions when deep cloning. Unless you are doing some fancy voodoo,
// this should remain at the default of false, as these often represent types. Don't
// mess with this unless you understand the consequences of cloning a type complete
// in-tact, which is almost never what you want to do. Optional.
//
// NOTE: This will fail to clone properties created using ECMA 5's Object.defineProperty function. Provided
// that defineProperty is used with adequate discretion, this should not become an issue. However, if
// some library developer decides that it would be fun lock down every last bit of API, you can either
// file a bug report or fire a developer. Such properties will be cloned as traditional properties
// iff they were defined using { enumerable: true }.
var clone = context.clone = function(obj, deep /* optional */, deepFunctions /* optional */) {
if (!(obj instanceof Object)) {
// Covers: undefined, null, passed-by-value, immutable
return obj;
}
// Normalize undefined.
if (undefined !== void(0)) {
var undefined = void(0);
}
if (deep === undefined || deep === null) {
// Default for deep parameter:
deep = false;
}
if (deepFunctions === undefined || deepFunctions === null) {
// If you change this, all of your users will switch to MSIE to spite you.
deepFunctions = false;
}
var copy;
if (obj instanceof Array || obj.toString() === '[object Arguments]') {
copy = [ ]; // Array-like
} else if (obj instanceof Function) {
copy = { }; // Object-like
}
function isEnumerableFallback(key) {
return !obj.constructor || !obj.constructor.prototype || obj[key] !== obj.constructor.prototype[key];
}
var isEnumerable = obj.propertyIsEnumerable || obj.hasOwnProperty || isEnumerableFallback;
for (var k in obj) {
if (!isEnumerable(k)) {
continue;
}
if (deep && obj[k] instanceof Object && !(!deepFunctions && obj[k] instanceof Function)) {
// Deep clone
copy[k] = this.clone(obj[k], deep, deepFunctions);
} else {
copy[k] = obj[k];
}
}
return copy;
};
// This allows clone() to be called as an instance method in the context of the object to be cloned.
// For example, ({ }).clone() would work as an alternative to Object.clone({ }). This purposely relies
// on an ECMA 5-compliant implementation of Object.defineProperty to avoid polluting enumerations, as
// nobody bothers checking ownership of properties in for-in enumerations, and rightfully so.
//
// In other words: this will deliberately fail in MSIE < 10.
if (attachPrototype && Object.defineProperty instanceof Function) {
Object.defineProperty(context.prototype, 'clone', {
__proto__: null,
configurable: true,
value: function(/* ... */) {
return clone.apply(context, Array.prototype.slice.apply(arguments, [ 0 ]).unshift(this));
}
});
}
})(
Object, // Bind context to Object, creating Object.clone. Change as necessary to avoid conflicts.
true // Attempt to attach to context.prototype (Object.prototype). See note above.
);

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.