-
Notifications
You must be signed in to change notification settings - Fork 1
/
udc.js
153 lines (125 loc) · 4.08 KB
/
udc.js
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
(function (root, factory) {
"use strict";
if (typeof exports === 'object') {
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
define(factory);
} else {
root.UltraDeepClone = factory();
}
}(this, function () {
var functionPropertyFilter = [
"caller",
"arguments"
];
// Node.js has a lot of silly enumerable properties on its "TypedArray" implementation
var typedArrayPropertyFilter = [
'BYTES_PER_ELEMENT',
'get',
'set',
'slice',
'subarray',
'buffer',
'length',
'byteOffset',
'byteLength'
];
var primitiveCloner = makeCloner(clonePrimitive);
var typedArrayCloner = makeRecursiveCloner(makeCloner(cloneTypedArray), typedArrayPropertyFilter);
function typeString (type) {
return '[object ' + type + ']';
}
var cloneFunctions = {};
cloneFunctions[typeString('RegExp')] = makeCloner(cloneRegExp);
cloneFunctions[typeString('Date')] = makeCloner(cloneDate);
cloneFunctions[typeString('Function')] = makeRecursiveCloner(makeCloner(cloneFunction), functionPropertyFilter);
cloneFunctions[typeString('Object')] = makeRecursiveCloner(makeCloner(cloneObject));
cloneFunctions[typeString('Array')] = makeRecursiveCloner(makeCloner(cloneArray));
['Null', 'Undefined', 'Number', 'String', 'Boolean']
.map(typeString)
.forEach(function (type) {
cloneFunctions[type] = primitiveCloner;
});
['Int8Array', 'Uint8Array', 'Uint8ClampedArray', 'Int16Array', 'Uint16Array',
'Int32Array', 'Uint32Array', 'Float32Array', 'Float64Array']
.map(typeString)
.forEach(function (type) {
cloneFunctions[type] = typedArrayCloner;
});
function makeArguments (numberOfArgs) {
var letters = [];
for ( var i = 1; i <= numberOfArgs; i++ ) letters.push("arg" + i);
return letters;
}
function wrapFunctionWithArity (callback) {
var argList = makeArguments(callback.length);
var functionCode = 'return false || function ';
functionCode += callback.name + '(';
functionCode += argList.join(', ') + ') {\n';
functionCode += 'return fn.apply(this, arguments);\n';
functionCode += '};'
return Function("fn", functionCode)(callback);
}
function makeCloner (cloneThing) {
return function(thing, thingStack, copyStack) {
thingStack.push(thing);
var copy = cloneThing(thing);
copyStack.push(copy);
return copy;
};
}
function clonePrimitive (primitive) {
return primitive;
}
function cloneRegExp (regexp) {
return new RegExp(regexp);
}
function cloneDate (date) {
return new Date(date.getTime());
}
// We can't really clone functions but we can wrap them in a new function that will
// recieve clones of any properties the original function may have had
function cloneFunction (fn) {
return wrapFunctionWithArity(fn);
}
// This will not properly clone `constructed` objects because
// it is impossible to know with what arguments the constructor
// was originally invoked.
function cloneObject (object) {
return Object.create(Object.getPrototypeOf(object));
}
function cloneArray (array) {
return [];
}
function cloneTypedArray (typedArray) {
var len = typedArray.length;
return new typedArray.constructor(len);
}
function makeRecursiveCloner (cloneThing, propertyFilter) {
return function(thing, thingStack, copyStack) {
var clone = this;
return Object.getOwnPropertyNames(thing)
.filter(function(prop){
return !propertyFilter || propertyFilter.indexOf(prop) === -1;
})
.reduce(function(copy, prop) {
var thingOffset = thingStack.indexOf(thing[prop]);
if (thingOffset === -1) {
copy[prop] = clone(thing[prop]);
} else {
copy[prop] = copyStack[thingOffset];
}
return copy;
}, cloneThing(thing, thingStack, copyStack));
};
}
return function _ultraDeepClone (source) {
var thingStack = [];
var copyStack = [];
function clone (thing) {
var typeOfThing = Object.prototype.toString.call(thing);
return cloneFunctions[typeOfThing].call(clone, thing, thingStack, copyStack);
};
return clone(source);
};
}));