(Das Folgende war hauptsächlich eine Integration von @ Maciej Bukowski , @ A. Levy , @ Jan Turo , @ Reduktion Antworten, und @ LeviRoberts , @ RobG Kommentare, vielen Dank an sie!!!)
Tiefe Kopie ? - JA! (meistens);
Seichte Kopie ? - NEIN! (außer Proxy
).
Ich heiße alle herzlich willkommen zum Testen clone()
.
Darüber hinaus, defineProp()
wurde entwickelt, um einfach und schnell (neu) definieren oder kopieren jede Art von Deskriptor.
Funktion
function clone(object) {
/*
Deep copy objects by value rather than by reference,
exception: `Proxy`
*/
const seen = new WeakMap()
return clone(object)
function clone(object) {
if (object !== Object(object)) return object /*
—— Check if the object belongs to a primitive data type */
if (object instanceof Node) return object.cloneNode(true) /*
—— Clone DOM trees */
let _object // The clone of object
switch (object.constructor) {
case Array:
case Object:
_object = cloneObject(object)
break
case Date:
_object = new Date(+object)
break
case Function:
_object = copyFn(object)
break
case RegExp:
_object = new RegExp(object)
break
default:
switch (Object.prototype.toString.call(object.constructor)) {
// // Stem from:
case "[object Function]":
switch (object[Symbol.toStringTag]) {
case undefined:
_object = cloneObject(object) // `class`
break
case "AsyncFunction":
case "GeneratorFunction":
case "AsyncGeneratorFunction":
_object = copyFn(object)
break
default:
_object = object
}
break
case "[object Undefined]": // `Object.create(null)`
_object = cloneObject(object)
break
default:
_object = object // `Proxy`
}
}
return _object
}
function cloneObject(object) {
if (seen.has(object)) return seen.get(object) /*
—— Handle recursive references (circular structures) */
const _object = Array.isArray(object)
? []
: Object.create(Object.getPrototypeOf(object)) /*
—— Assign [[Prototype]] for inheritance */
seen.set(object, _object) /*
—— Make `_object` the associative mirror of `object` */
Reflect.ownKeys(object).forEach(key =>
defineProp(_object, key, { value: clone(object[key]) }, object)
)
return _object
}
}
function copyPropDescs(target, source) {
Object.defineProperties(target,
Object.getOwnPropertyDescriptors(source)
)
}
function convertFnToStr(fn) {
let fnStr = String(fn)
if (fn.name.startsWith("[")) // isSymbolKey
fnStr = fnStr.replace(/\[Symbol\..+?\]/, '')
fnStr = /^(?!(async )?(function\b|[^{]+?=>))[^(]+?\(/.test(fnStr)
? fnStr.replace(/^(async )?(\*)?/, "$1function$2 ") : fnStr
return fnStr
}
function copyFn(fn) {
const newFn = new Function(`return ${convertFnToStr(fn)}`)()
copyPropDescs(newFn, fn)
return newFn
}
function defineProp(object, key, descriptor = {}, copyFrom = {}) {
const { configurable: _configurable, writable: _writable }
= Object.getOwnPropertyDescriptor(object, key)
|| { configurable: true, writable: true }
const test = _configurable // Can redefine property
&& (_writable === undefined || _writable) // Can assign to property
if (!test || arguments.length <= 2) return test
const basisDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
|| { configurable: true, writable: true } // Custom…
|| {}; // …or left to native default settings
["get", "set", "value", "writable", "enumerable", "configurable"]
.forEach(attr =>
descriptor[attr] === undefined &&
(descriptor[attr] = basisDesc[attr])
)
const { get, set, value, writable, enumerable, configurable }
= descriptor
return Object.defineProperty(object, key, {
enumerable, configurable, ...get || set
? { get, set } // Accessor descriptor
: { value, writable } // Data descriptor
})
}
// Tests
const obj0 = {
u: undefined,
nul: null,
t: true,
num: 9,
str: "",
sym: Symbol("symbol"),
[Symbol("e")]: Math.E,
arr: [[0], [1, 2]],
d: new Date(),
re: /f/g,
get g() { return 0 },
o: {
n: 0,
o: { f: function (...args) { } }
},
f: {
getAccessorStr(object) {
return []
.concat(...
Object.values(Object.getOwnPropertyDescriptors(object))
.filter(desc => desc.writable === undefined)
.map(desc => Object.values(desc))
)
.filter(prop => typeof prop === "function")
.map(String)
},
f0: function f0() { },
f1: function () { },
f2: a => a / (a + 1),
f3: () => 0,
f4(params) { return param => param + params },
f5: (a, b) => ({ c = 0 } = {}) => a + b + c
}
}
defineProp(obj0, "s", { set(v) { this._s = v } })
defineProp(obj0.arr, "tint", { value: { is: "non-enumerable" } })
obj0.arr[0].name = "nested array"
let obj1 = clone(obj0)
obj1.o.n = 1
obj1.o.o.g = function g(a = 0, b = 0) { return a + b }
obj1.arr[1][1] = 3
obj1.d.setTime(+obj0.d + 60 * 1000)
obj1.arr.tint.is = "enumerable? no"
obj1.arr[0].name = "a nested arr"
defineProp(obj1, "s", { set(v) { this._s = v + 1 } })
defineProp(obj1.re, "multiline", { value: true })
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Routinely")
console.log("obj0:\n ", JSON.stringify(obj0))
console.log("obj1:\n ", JSON.stringify(obj1))
console.log()
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log()
console.log("obj0\n ",
".arr.tint:", obj0.arr.tint, "\n ",
".arr[0].name:", obj0.arr[0].name
)
console.log("obj1\n ",
".arr.tint:", obj1.arr.tint, "\n ",
".arr[0].name:", obj1.arr[0].name
)
console.log()
console.log("Accessor-type descriptor\n ",
"of obj0:", obj0.f.getAccessorStr(obj0), "\n ",
"of obj1:", obj1.f.getAccessorStr(obj1), "\n ",
"set (obj0 & obj1) .s :", obj0.s = obj1.s = 0, "\n ",
" (obj0 , obj1) ._s:", obj0._s, ",", obj1._s
)
console.log("—— obj0 has not been interfered.")
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - More kinds of functions")
const fnsForTest = {
f(_) { return _ },
func: _ => _,
aFunc: async _ => _,
async function() { },
async asyncFunc() { },
aFn: async function () { },
*gen() { },
async *asyncGen() { },
aG1: async function* () { },
aG2: async function* gen() { },
*[Symbol.iterator]() { yield* Object.keys(this) }
}
console.log(Reflect.ownKeys(fnsForTest).map(k =>
`${String(k)}:
${fnsForTest[k].name}-->
${String(fnsForTest[k])}`
).join("\n"))
const normedFnsStr = `{
f: function f(_) { return _ },
func: _ => _,
aFunc: async _ => _,
function: async function() { },
asyncFunc: async function asyncFunc() { },
aFn: asyc function () { },
gen: function* gen() { },
asyncGen: async function* asyncGen() { },
aG1: async function* () { },
aG2: async function* gen() { },
[Symbol.iterator]: function* () { yield* Object.keys(this) }
}`
const copiedFnsForTest = clone(fnsForTest)
console.log("fnsForTest:", fnsForTest)
console.log("fnsForTest (copied):", copiedFnsForTest)
console.log("fnsForTest (normed str):", eval(`(${normedFnsStr})`))
console.log("Comparison of fnsForTest and its clone:",
Reflect.ownKeys(fnsForTest).map(k =>
[k, fnsForTest[k] === copiedFnsForTest[k]]
)
)
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Circular structures")
obj0.o.r = {}
obj0.o.r.recursion = obj0.o
obj0.arr[1] = obj0.arr
obj1 = clone(obj0)
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log("Clear obj0's recursion:",
obj0.o.r.recursion = null, obj0.arr[1] = 1
)
console.log(
"obj0\n ",
".o.r:", obj0.o.r, "\n ",
".arr:", obj0.arr
)
console.log(
"obj1\n ",
".o.r:", obj1.o.r, "\n ",
".arr:", obj1.arr
)
console.log("—— obj1 has not been interfered.")
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Classes")
class Person {
constructor(name) {
this.name = name
}
}
class Boy extends Person { }
Boy.prototype.sex = "M"
const boy0 = new Boy
boy0.hobby = { sport: "spaceflight" }
const boy1 = clone(boy0)
boy1.hobby.sport = "superluminal flight"
boy0.name = "one"
boy1.name = "neo"
console.log("boy0:\n ", boy0)
console.log("boy1:\n ", boy1)
console.log("boy1's prototype === boy0's:",
Object.getPrototypeOf(boy1) === Object.getPrototypeOf(boy0)
)
Referenzen
Object.create()
| MDN
Object.defineProperties()
| MDN
- Aufzählbarkeit und Eigentum von Eigenschaften | MDN
- TypeError: zyklischer Objektwert | MDN
Verwendete Sprachtricks
- Bedingte Hinzufügung einer Requisite zum Objekt
35 Stimmen
Siehe diese Frage: stackoverflow.com/questions/122102/
0 Stimmen
Unterstützen Sie auf jeden Fall @Niyaz! Shortlink: tinyurl.com/JSCopyObject
290 Stimmen
Für JSON verwende ich
mObj=JSON.parse(JSON.stringify(jsonObject));
79 Stimmen
Ich verstehe wirklich nicht, warum niemand vorschlägt.
Object.create(o)
dass es alles tut, was der Autor verlangt?5 Stimmen
@froginvasion Wahrscheinlich, weil es im IE8 und darunter nicht unterstützt wird.
4 Stimmen
@Wynand Das ist überhaupt kein gutes Argument. Es gibt eine Menge Polyfills für
Object.create
die zum Beispiel gefunden werden können: refheap.com/900061 Stimmen
@froginvasion In Ordnung, obwohl ich argumentieren könnte, dass der Auftraggeber eine "elegante" [ sic und nicht jeder würde notwendigerweise zustimmen, dass die Verwendung eines Polyfills dieser Beschreibung entspricht. Ich habe nur versucht, Ihnen Folgendes mitzuteilen a möglicher Grund dafür
Object.create
wurde nicht vorgeschlagen (falls dies der Fall ist), weshalb ich "wahrscheinlich" schrieb. Unabhängig davon bin ich für Polyfills und habe mir vor kurzem Folgendes angesehen diese eine, die übrigens ähnlich wie Ihr Schnipsel ist. Wenn Sie ein besseres Feedback wünschen, könnten Sie Ihren Vorschlag auch als Antwort hinzufügen.2 Stimmen
Object.create eignet sich hervorragend, wenn Sie den Prototyp Ihres Objekts kopieren möchten.
59 Stimmen
var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2;
Nachdem Sie dies getan haben,y.deep.key
wird auch 2 sein, daher kann Object.create NICHT zum Klonen verwendet werden...1 Stimmen
@froginvasion - weil es nicht einmal für flache Objekte funktioniert? (Chrome 45). Du bekommst einfach
__proto__
nicht ein Klon...2 Stimmen
Object.create
bekommt man nicht__proto__
wird ein neues Objekt erzeugt, dessen Prototyp auf das Argument verweist, wodurch eine Kette entsteht. Ich stimme zu, dass es sich vielleicht nicht um einen echten "Klon" im traditionellen Sinne handelt, aber je nach SituationObject.create
ist viel konsistenter als die verschiedenen Klontechniken, die im Folgenden verwendet werden. developer.mozilla.org/de-US/docs/Web/JavaScript/Reference/1 Stimmen
Wie wäre es mit
var newObj = (function(){ return oldObj; }());
23 Stimmen
@r3wt das wird nicht funktionieren... Bitte posten Sie erst, nachdem Sie einen grundlegenden Test der Lösung durchgeführt haben...
2 Stimmen
Hier ein Benchmark von verschiedenen Lösungen: jsben.ch/#/bWfk9
0 Stimmen
@RubenStolk warum kann OP das Objekt nicht in eine neue Variable speichern?
var copyobject=object
??4 Stimmen
@Mahi Dadurch wird das Objekt nicht kopiert. Es wird eine neue Variable erstellt, die auf das gleiche Objekt verweist.
0 Stimmen
@LordLoh. Was ist, wenn Sie in einer Javascript-Umgebung sind, in der Sie keine Bibliotheken wie JSON importieren können. Apples Quartz Composer Editor zum Beispiel, nicht sicher über Adobe CC Scripting.
1 Stimmen
developer.mozilla.org/de-US/docs/Web/JavaScript/Reference/ - Ich habe das gerade mit dem Chrome-Entwickler-Terminal ausprobiert -
oj1={}; oj1.p1=222; oj2={}; Object.assign(oj2,oj1); oj2.p1=555; oj1.p1;
- wird dies zeigen222
Auch hier wird geantwortet: stackoverflow.com/a/36177142/4821760 Stimmen
@LordLoh. Anscheinend kann ich JSON.parse und JSON.stringify doch innerhalb von QC aufrufen. Core Funktionen aufrufen. Auch habe ich dieses Polyfill auf der Referenzseite und Links zu erweiterten Polyfills JSON2 und JSON3 gefunden: developer.mozilla.org/de-US/docs/Web/JavaScript/Reference/
3 Stimmen
{...original}
6 Stimmen
Wenn Sie Object.create() vorschlagen, dann wissen Sie sehr wenig über JS. Mit Object.create wird das Objekt X nicht geklont. Y = Object.create(X) tut dies und nichts weiter: Es wird ein neues Objekt Y mit X als Prototyp erstellt. Das Objekt X wird zum Prototyp von Objekt Y. Das ist nicht das, was Sie wollen. Ganz und gar nicht. Da die Methoden und Daten von X nicht nach Y kopiert werden, sondern referenziert werden, enthält Y eine Referenz auf die Daten und Methoden von X. Wie falsch kann man liegen!?
0 Stimmen
Kürzlich beantwortete es über aquí
0 Stimmen
Ver developer.mozilla.org's JavaScript/Referenz von assign#Deep_Clone .
0 Stimmen
Dadurch sollte eine Kopie erstellt werden:
let y = {...x};
0 Stimmen
Sie können die Clone-Methode von Lodash oder den Spread-Operator verwenden let x = {} let y = {irgendein Wert} x = {...x, ...y}
0 Stimmen
stackoverflow.com/questions/38416020/