Як правильно клонувати об’єкт JavaScript?


3075

У мене є об’єкт x. Я хотів би скопіювати його як об'єкт y, щоб зміни yне змінювались x. Я зрозумів, що копіювання об’єктів, отриманих із вбудованих JavaScript-об'єктів, призведе до зайвих, небажаних властивостей. Це не проблема, оскільки я копіюю один із власних буквених об’єктів.

Як правильно клонувати об’єкт JavaScript?


30
Дивіться це запитання: stackoverflow.com/questions/122102/…
Ніяз

256
Для JSON я використовуюmObj=JSON.parse(JSON.stringify(jsonObject));
лорд Лох.

67
Я насправді не розумію, чому ніхто не пропонує Object.create(o), вона робить все, про що пише автор?
froginvasion

43
var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2; Після цього y.deep.keyбуде також 2, отже Object.create НЕ МОЖЕ ВИКОРИСТОВАТИ для клонування ...
Рубен Столк

17
@ r3wt, що не буде працювати ... Будь ласка, публікуйте лише після базового тестування рішення ..
akshay

Відповіді:


1561

Зробити це для будь-якого об’єкта в JavaScript не буде просто і просто. Ви зіткнетеся з проблемою помилкового підбору атрибутів з прототипу об'єкта, які слід залишити в прототипі, а не скопіювати в новий екземпляр. Якщо, наприклад, ви додаєте cloneметод Object.prototype, як зображено у деяких відповідях, вам потрібно буде чітко пропустити цей атрибут. Але що робити, якщо до них додаються інші додаткові методи Object.prototypeабо інші проміжні прототипи, про які ви не знаєте? У такому випадку ви скопіюєте атрибути, які ви не повинні, тому вам потрібно виявити непередбачені, не локальні атрибути hasOwnPropertyметодом.

На додаток до нечисленних атрибутів, ви спробуєте скопіювати проблему, коли намагатиметесь скопіювати об'єкти, які мають приховані властивості. Наприклад, prototypeє прихованою властивістю функції. Крім того, на прототип об'єкта посилається атрибут __proto__, який також прихований, і він не буде скопійований ітерацією циклу for / in над атрибутами вихідного об'єкта. Я думаю, що це __proto__може бути специфічно для інтерпретатора JavaScript Firefox, і це може бути щось інше в інших браузерах, але ви отримаєте малюнок. Не все перелічено. Ви можете скопіювати прихований атрибут, якщо знаєте його ім'я, але я не знаю жодного способу його автоматичного виявлення.

Ще одна затяжка у прагненні до елегантного рішення - це проблема правильного налаштування успадкування прототипу. Якщо прототип вашого вихідного об'єкта є Object, то просто створити новий загальний об'єкт з {}буде працювати, але якщо прототип джерела є деяким нащадком Object, то вам не вистачає додаткових членів того прототипу, який ви пропустили за допомогою hasOwnPropertyфільтра, або який були в прототипі, але в першу чергу їх не було. Одним з варіантів може бути виклик constructorвластивості вихідного об'єкта, щоб отримати початковий об'єкт копіювання, а потім скопіювати атрибути, але тоді ви все одно не отримаєте нечисленних атрибутів. Наприклад, Dateоб’єкт зберігає свої дані як прихований член:

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

Рядок дати для d1буде на 5 секунд відстань від мети d2. Спосіб зробити Dateте ж саме, що й інший - за допомогою виклику setTimeметоду, але це специфічно для Dateкласу. Я не думаю, що існує куленебезпечне загальне рішення цієї проблеми, хоча я би радий помилитися!

Коли я повинен був здійснювати загальне глибоке копіювання я в кінцевому підсумку під загрозу, якщо припустити , що я тільки потрібно скопіювати простий Object, Array, Date, String, Number, або Boolean. Останні 3 типи незмінні, тому я міг виконати дрібну копію і не хвилюватися про її зміну. Далі я припускав, що будь-які елементи, що містяться у Objectабо Arrayбудуть також одним із шести простих типів у цьому списку. Це можна досягти за допомогою такого коду:

function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

Вищенаведена функція спрацює належним чином для 6 згаданих я простих типів, доки дані в об'єктах та масивах утворюють структуру дерева. Тобто в об’єкті не більше однієї посилання на ті самі дані. Наприклад:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

Він не зможе обробити жоден JavaScript-об’єкт, але це може бути достатньо для багатьох цілей, доки ви не вважаєте, що він буде працювати лише для всього, що ви на нього кинете.


5
майже добре працював у nodejs - просто довелося змінити рядок для (var i = 0, var len = obj.length; i <len; ++ i) {to for (var i = 0; i <obj.length; ++ i) {
Trindaz

5
Для майбутніх googlers: така ж глибока копія, передача посилань рекурсивно, а не використання тверджень "return" на сайті gist.github.com/2234277
Trindaz

7
Чи JSON.parse(JSON.stringify([some object]),[some revirer function])було б сьогодні рішенням?
KooiInc

5
У першому фрагменті ви впевнені, що цього не повинно бути var cpy = new obj.constructor()?
cyon

8
Призначення об’єкта, схоже, не робить справжню копію в Chrome, підтримує посилання на оригінальний об'єкт - в кінцевому підсумку використовуючи JSON.stringify та JSON.parse для клонування - працював ідеально
1owk3y

974

Якщо ви не використовуєте Dates, функції, undefined, regExp або Infinity у вашому об'єкті, дуже простий один вкладиш JSON.parse(JSON.stringify(object)):

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

Це працює для всіх видів об’єктів, що містять об'єкти, масиви, рядки, булеві числа та числа.

Дивіться також цю статтю про структурований алгоритм клонування браузерів, який використовується при публікації повідомлень працівника та від нього. Він також містить функцію глибокого клонування.


42
Зауважте, що це можна використовувати лише для тестування. По-перше, це далеко не оптимально з точки зору споживання часу та пам’яті. По-друге, не у всіх браузерах є такі методи.
Nux

2
Ви завжди можете включити JSON2.js або JSON3.js. Вони вам знадобляться для вашого додатка в будь-якому випадку. Але я згоден, це може бути не найкращим рішенням, оскільки JSON.stringify не включає успадковані властивості.
Тім Гонг

78
@Nux, чому б не оптимально за часом та пам’яттю? MiJyn каже: "Причина, чому цей метод повільніше, ніж дрібне копіювання (на глибокому об'єкті), полягає в тому, що цей метод, за визначенням, є копіями. Але оскільки JSON реалізований в нативному коді (у більшості браузерів), це буде значно швидше ніж використання будь-якого іншого рішення для глибокого копіювання на основі JavaScript, а іноді може бути швидшим, ніж метод дрібного копіювання на основі JavaScript (див .: jsperf.com/cloning-an-object/79). " stackoverflow.com/questions/122102 / ...
BeauCielBleu

16
Я просто хочу додати оновлення до цього на жовтень 2014. Chrome 37+ швидше за допомогою JSON.parse (JSON.stringify (oldObject)); Перевага від використання цього полягає в тому, що для двигуна javascript дуже легко бачити та оптимізувати щось краще, якщо воно хоче.
mirhagk

17
Оновлення 2016 року: тепер це повинно працювати майже в кожному браузері, який широко використовується. (див. Чи можу я використовувати ... ) Головним питанням було б, чи є вона достатньо ефективною.
Джеймс Фостер

783

За допомогою jQuery ви можете дрібну копію з розширенням :

var copiedObject = jQuery.extend({}, originalObject)

наступні зміни до copiedObjectзаповіту не вплинуть на originalObject, і навпаки.

Або зробити глибоку копію :

var copiedObject = jQuery.extend(true, {}, originalObject)

164
або навіть:var copiedObject = jQuery.extend({},originalObject);
Грант Маклін

82
Також корисно вказати істину як перший парам для глибокої копії:jQuery.extend(true, {}, originalObject);
Буде Шейвер

6
Так, я вважаю це посилання корисним (те саме рішення, що й Паскаль) stackoverflow.com/questions/122102/…
Garry English

3
Лише зауважте, це не копіює протоконструктор оригінального об’єкта
Сем Джонс

1
За даними stackoverflow.com/questions/184710/… , здається, що "мілка копія" просто скопіює посилання на originalObject, так чому тут це говорить ...subsequent changes to the copiedObject will not affect the originalObject, and vice versa.... Вибачте, що я дуже розгубився.
Карр

685

У ECMAScript 6 є метод Object.assign , який копіює значення всіх перелічених власних властивостей з одного об'єкта на інший. Наприклад:

var x = {myProp: "value"};
var y = Object.assign({}, x); 

Але майте на увазі, що вкладені об'єкти все ще копіюються як посилання.


Так, я вважаю, що Object.assignце шлях. Його також легко заповнити

22
Також майте в виду , що це буде копіювати «методи» , певні з допомогою літералів об'єктів (оскільки вони є перечисли) , але НЕ методи кинули виклик через механізм «класу» (так як вони НЕ перелічуваних).
Маркус Юній Брут

16
Я думаю, слід зазначити, що це не підтримується IE, крім Edge. Деякі люди все ще використовують це.
Саулій

1
Це те саме, що @EugeneTiurin його відповідь.
Загинув

5
Якщо говорити з досвіду тут ... НЕ використовуйте це. Об'єкти розроблені так, щоб бути ієрархічними, і ви будете копіювати лише перший рівень, що приводить вас до призначення всього об'єкта, скопійованого. Повірте мені, це може врятувати вас від дряпання голови.
ow3n

232

На MDN :

  • Якщо ви хочете дрібну копію, використовуйте Object.assign({}, a)
  • Для "глибокої" копії використовуйте JSON.parse(JSON.stringify(a))

У зовнішніх бібліотеках немає потреби, але спочатку потрібно перевірити сумісність браузера .


8
JSON.parse (JSON.stringify (a)) виглядає красиво, але перед його використанням я рекомендую порівняльний аналіз часу, необхідного для потрібної колекції. Залежно від розміру об'єкта, це може бути не найшвидшим варіантом.
Едза

3
Метод JSON Я помітив, що перетворює об'єкти дати в рядки, але не повертається до дат. Доводиться стикатися із забавкою часових поясів у Javascript та вручну фіксувати будь-які дати. Можуть бути подібні випадки і для інших типів, окрім дат
Стів Зегер

для Date, я б використовував moment.js, оскільки він має cloneфункціонал. дивіться більше тут momentjs.com/docs/#/parsing/moment-clone
Тарек

Це добре, але будьте уважні, якщо об’єкт має функції всередині
lesolorzanov

Інша проблема розбору json - це кругові посилання. Якщо є якісь кругові посилання всередині об’єкта, це порушиться
philoj

133

Відповідей багато, але жоден, який згадує Object.create з ECMAScript 5, який, правда, не дає точної копії, але встановлює джерело як прототип нового об'єкта.

Таким чином, це не точна відповідь на питання, але це однолінійне рішення і, таким чином, елегантне. І найкраще це працює в 2 випадках:

  1. Де таке спадок корисне (так!)
  2. Якщо вихідний об'єкт не буде змінено, таким чином, відношення між двома об'єктами не буде проблемою.

Приклад:

var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property

Чому я вважаю це рішення вищим? Це рідне, таким чином, ніяких циклів, ніяких рекурсій. Однак старшим браузерам знадобиться поліфайл.


Примітка: Object.create не є глибокою копією (в itpastorn згадується відсутність рекурсії). Доказ:var a = {b:'hello',c:{d:'world'}}, b = Object.create(a); a == b /* false */; a.c == b.c /* true */;
замнуть

103
Це прототипічне успадкування, а не клонування. Це абсолютно різні речі. У нового об'єкта немає власних властивостей, він просто вказує на властивості прототипу. Сенс клонування полягає у створенні нового свіжого об’єкта, який не посилається ні на які властивості іншого об’єкта.
d13

7
Я повністю з вами згоден. Я також згоден, що це не клонування, як могло б бути «задумано». Але давайте люди, сприймайте природу JavaScript, а не намагайтеся знайти незрозумілі рішення, які не стандартизовані. Звичайно, вам не подобаються прототипи, і всі вони "благ" для вас, але насправді вони дуже корисні, якщо ви знаєте, що ви робите.
froginvasion

4
@RobG: Ця стаття пояснює різницю між посиланням та клонуванням: en.wikipedia.org/wiki/Cloning_(programming) . Object.createвказує на властивості батьків через посилання. Це означає, що якщо значення властивостей батьків змінюються, дитина також зміниться. Це має деякі дивовижні побічні ефекти з вкладеними масивами та об’єктами, які можуть призвести до важко знайти помилок у вашому коді, якщо ви про них не знаєте: jsbin.com/EKivInO/2 . Клонований об’єкт - це абсолютно новий, незалежний об’єкт, який має ті ж властивості та значення, що і батьківський, але не пов'язаний з батьківським.
d13

1
Це вводить людей в оману ... Object.create () може використовуватися як засіб для спадкування, але клонування ніде не знаходиться.
prajnavantha

128

Елегантний спосіб клонування об’єкта Javascript в одному рядку коду

Object.assignМетод є частиною 2015 (ES6) стандарту ECMAScript і робить саме те , що вам потрібно.

var clone = Object.assign({}, obj);

Метод Object.assign () використовується для копіювання значень усіх перелічених власних властивостей з одного або декількох вихідних об'єктів у цільовий об'єкт.

Детальніше ...

Polyfill для підтримки старих браузерів:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}

Вибачте за тупе запитання, але чому Object.assignприймає два параметри, коли valueфункція в поліфайлі приймає лише один параметр?
Qwertie

@Qwertie вчора Всі аргументи переглянуті і об'єднані в один об’єкт, пріоритетуючи властивості з останнього переданого аргументу
Євген Тюрін

О, я бачу, дякую (я раніше не був знайомий з argumentsоб'єктом.) У мене виникають проблеми з пошуком Object()через Google ... це машинопис, чи не так?
Qwertie

44
це здійснить лише неглибоке «клонування»
Маркус Юніус Брут

1
Ця відповідь точно така ж, як і ця: stackoverflow.com/questions/122102/… Я знаю, що це та сама людина, але вам слід посилатись, а не просто копіювати відповідь.
lesolorzanov

86

В Інтернеті є декілька проблем з більшістю рішень. Тож я вирішив здійснити подальшу діяльність, яка включає, чому прийняту відповідь не слід приймати.

вихідна ситуація

Я хочу копіювати Javascript Objectз усіма його дітьми та їхніми дітьми тощо. Але так як я не вид нормального розробника, мій Objectмає нормальний properties , circular structuresі навітьnested objects .

Тож давайте створимо а circular structureі nested objectперше.

function Circ() {
    this.me = this;
}

function Nested(y) {
    this.y = y;
}

Зведемо все разом у Objectназваний a.

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};

Далі ми хочемо скопіювати aзмінну з назвою bта вимкнути її.

var b = a;

b.x = 'b';
b.nested.y = 'b';

Ви знаєте, що тут сталося, тому що якби не ви навіть не зупинилися на цьому великому питанні.

console.log(a, b);

a --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

Тепер давайте знайдемо рішення.

JSON

Першою спробою я спробував скористатися JSON.

var b = JSON.parse( JSON.stringify( a ) );

b.x = 'b';
b.nested.y = 'b';

Не витрачайте на це занадто багато часу, ви отримаєте TypeError: Converting circular structure to JSON .

Рекурсивна копія (прийнята "відповідь")

Давайте подивимось на прийняту відповідь.

function cloneSO(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

Добре виглядає, так? Це рекурсивна копія об'єкта та обробляє й інші типи, як Date, але це не було вимогою.

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';

Рекурсія і circular structuresне працює добре разом ...RangeError: Maximum call stack size exceeded

рідне рішення

Сперечавшись із моїм колегою, мій бос запитав нас, що сталося, і він знайшов просте рішення після деякого гугла. Це називається Object.create.

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';

Це рішення було додано до Javascript деякий час тому і навіть обробляє circular structure.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

... і бачите, це не працювало з вкладеною структурою всередині.

поліфіл для рідного розчину

У Object.createстаршому веб-переглядачі є полізаповнення, як і у IE 8. Це щось на зразок рекомендованого Mozilla, і, звичайно, воно не є ідеальним і призводить до тієї ж проблеми, що і в нашому рішенні .

function F() {};
function clonePF(o) {
    F.prototype = o;
    return new F();
}

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';

Я поставив Fпоза рамки, щоб ми могли подивитися, що instanceofнам каже.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> F {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> true

Така ж проблема, як і вроджене рішення , але трохи гірший вихід.

краще (але не ідеальне) рішення

Під час копання я знайшов подібне запитання ( У Javascript при виконанні глибокої копії, як я уникаю циклу через те, що властивість "це"? ) До цього, але з кращим рішенням.

function cloneDR(o) {
    const gdcc = "__getDeepCircularCopy__";
    if (o !== Object(o)) {
        return o; // primitive value
    }

    var set = gdcc in o,
        cache = o[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    o[gdcc] = function() { return result; }; // overwrite
    if (o instanceof Array) {
        result = [];
        for (var i=0; i<o.length; i++) {
            result[i] = cloneDR(o[i]);
        }
    } else {
        result = {};
        for (var prop in o)
            if (prop != gdcc)
                result[prop] = cloneDR(o[prop]);
            else if (set)
                result[prop] = cloneDR(cache);
    }
    if (set) {
        o[gdcc] = cache; // reset
    } else {
        delete o[gdcc]; // unset again
    }
    return result;
}

var b = cloneDR(a);

b.x = 'b';
b.nested.y = 'b';

І давайте подивимось на результат ...

console.log(a, b);

a --> Object {
    x: "a",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "a"
    }
}

b --> Object {
    x: "b",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> false

Вимоги відповідають, але все ж є деякі менші проблеми, включаючи зміну instanceположення nestedта circна Object.

Структура дерев, які діляться листям, не буде скопійована, вони стануть двома незалежними листками:

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

висновок

Останнє рішення з використанням рекурсії та кешу, можливо, не найкраще, але це справжня глибока копія об'єкта. Він обробляє простий properties, circular structuresі nested object, але це зіпсує екземпляр з них при клонуванні.

jsfiddle


12
тож суперечка - уникати цієї проблеми :)
mikus

@mikus, поки не з’явиться справжня специфікація, яка охоплює більше, ніж просто основні випадки використання, так.
Фабіо Полоні

2
Правильний аналіз запропонованих вище рішень, але висновок, зроблений автором, свідчить про відсутність вирішення цього питання.
Амір Мог

2
Соромно, що JS не включає в себе функцію нативного клонування.
l00k

1
Серед усіх найкращих відповідей я вважаю, що це близьке до правильного.
КТУ

77

Якщо у вас все з дрібною копією, у бібліотеці underscore.js є метод клонування .

y = _.clone(x);

або ви можете розширити, як

copiedObject = _.extend({},originalObject);

2
Дякую. Використання цієї методики на сервері Meteor.
Турбо

Для швидкого початку роботи з lodash я рекомендую вивчити npm, Browrify, а також lodash. Мені дісталося клонування для роботи з 'npm i --save lodash.clone', а потім 'var clone = requ (' lodash.clone ');' Щоб вимагати роботи, вам потрібно щось на зразок перегляду. Як тільки ви встановите його і дізнаєтесь, як він працює, ви будете використовувати "переглядати файл yourfile.js> bundle.js; start chrome index.html" щоразу, коли ви запускаєте код (замість того, щоб переходити в Chrome). Це збирає ваш файл і всі потрібні вам файли з npm-модуля в bundle.js. Ви можете, можливо, заощадити час і автоматизувати цей крок за допомогою Gulp.
Аарон Белл

65

Гаразд, уявіть, що у вас є цей об'єкт нижче, і ви хочете його клонувати:

let obj = {a:1, b:2, c:3}; //ES6

або

var obj = {a:1, b:2, c:3}; //ES5

відповідь в основному depeneds , на якому ECMAScript ви використовуєте, в ES6+, ви можете просто використовувати , Object.assignщоб зробити клон:

let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};

або використовуючи такий оператор розповсюдження:

let cloned = {...obj}; //new {a:1, b:2, c:3};

Але якщо ви використовуєте ES5, ви можете використовувати декілька методів, але JSON.stringify, переконайтеся, що ви не використовуєте для копіювання великих фрагментів даних, але у багатьох випадках це може бути зручним способом одним рядком:

let cloned = JSON.parse(JSON.stringify(obj)); 
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over

Чи можете ви надати приклад того, що big chunk of dataб прирівнювалося? 100кб? 100 Мб? Дякую!
користувач1063287

Так, @ user1063287, що в основному більші дані, продуктивність гірша ... тому це дійсно залежить, а не kb, mb чи gb, це більше про те, скільки разів ви хочете це зробити також ... Також це не працюватиме для функцій та інших матеріалів ...
Аліреза

3
Object.assignробить дрібну копію (подібно до розповсюдження, @Alizera)
Богдан Д

Ви не можете використовувати вхід es5: ^) @Alireza
SensationSama

40

Одне особливо неелегантне рішення - використовувати кодування JSON для створення глибоких копій об'єктів, які не мають методів-членів. Методика полягає в тому, щоб кодувати JSON ваш цільовий об'єкт, потім, розшифрувавши його, ви отримаєте шукану копію. Ви можете розшифрувати стільки разів, скільки хочете зробити стільки копій, скільки вам потрібно.

Звичайно, функції не належать до JSON, тому це працює лише для об'єктів без членських методів.

Ця методологія була ідеальною для мого використання, оскільки я зберігаю краплі JSON у сховищі ключових значень, і коли вони відкриваються як об’єкти в API JavaScript, кожен об'єкт фактично містить копію початкового стану об’єкта, тому ми може обчислити дельту після того, як абонент мутував відкритий об'єкт.

var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value

Чому функції не належать до JSON? Я бачив їх, як вони передавались як JSON більше одного разу ...
the_drow

5
Функції не є частиною спекуляції JSON, вони не є безпечним (або розумним) способом передачі даних, для чого був створений JSON. Я знаю, що вбудований кодер JSON у Firefox просто ігнорує передані йому функції, але я не впевнений у поведінці інших.
Кріс Уокер

1
@mark: { 'foo': function() { return 1; } }об'єкт, побудований буквально.
abarnert

@abarnert функції не є даними. "Функціональні літерали" - це неправильне слово, оскільки функції можуть містити довільний код, включаючи призначення та всілякі "не серіалізаційні" речі.
vemv

35

Ви можете просто використовувати властивість розповсюдження для копіювання об'єкта без посилань. Але будьте обережні (див. Коментарі), "копія" знаходиться лише на найнижчому рівні об'єкта / масиву. Вкладені властивості все ще є посиланнями!


Повний клон:

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'

Клон із посиланнями на другий рівень:

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'

JavaScript насправді не підтримує глибоких клонів. Використовуйте функцію утиліти. Наприклад Ramda:

http://ramdajs.com/docs/#clone


1
Це не працює ... це, мабуть, спрацює, коли x буде масивом, наприклад x = ['ab', 'cd', ...]
Kamil Kiełczewski

3
Це працює, але майте на увазі, що це SHALLOW копія, тому будь-які глибокі посилання на інші об'єкти залишаються посиланнями!
Багз Зайчик

Частковий клон може трапитися і таким чином:const first = {a: 'foo', b: 'bar'}; const second = {...{a} = first}
Крістіан Трена

25

Для тих, хто використовує AngularJS, існує також прямий метод клонування або розширення об'єктів у цій бібліотеці.

var destination = angular.copy(source);

або

angular.copy(source, destination);

Детальніше в документації на angular.copy ...


2
Це глибока копія FYI.
замнуть

22

Ось функція, яку ви можете використовувати.

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}

10
Ця відповідь досить близька, але не зовсім правильна. Якщо ви спробуєте клонувати об’єкт Date, ви не отримаєте тієї самої дати, оскільки виклик функції конструктора Date ініціалізує нову дату з поточною датою / часом. Це значення не перелічується і не буде скопійовано циклом for / in.
А. Леві

Не ідеально, але приємно для тих основних випадків. Наприклад, що дозволяє просто клонувати аргумент, який може бути базовим об'єктом, масивом або рядком.
james_womack

Запропоновано правильним викликом конструктора за допомогою new. Прийнята відповідь не відповідає.
GetFree

працює над вузлом все інше! як і раніше залишаються посилання
користувач956584

Рекурсивна думка чудова. Але якщо значення масиву, воно буде працювати?
Q10Вікінг

22

Відповідь А.Леві майже повна, ось мій невеликий внесок: є спосіб поводження з рекурсивними посиланнями , дивіться цей рядок

if(this[attr]==this) copy[attr] = copy;

Якщо об’єктом є XML-елемент DOM, ми повинні використовувати замість нього cloneNode

if(this.cloneNode) return this.cloneNode(true);

Натхненний вичерпним дослідженням А.Леві та прототипізацією підходу Кальвіна, я пропоную таке рішення:

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

Дивіться також записку Енді Берка у відповідях.


3
Date.prototype.clone = function() {return new Date(+this)};
RobG

22

З цієї статті: Як скопіювати масиви та об’єкти в Javascript від Брайана Хуйсмана:

Object.prototype.clone = function() {
  var newObj = (this instanceof Array) ? [] : {};
  for (var i in this) {
    if (i == 'clone') continue;
    if (this[i] && typeof this[i] == "object") {
      newObj[i] = this[i].clone();
    } else newObj[i] = this[i]
  } return newObj;
};

4
Це близько, але не працює для жодного об’єкта. Спробуйте клонувати об’єкт Дата. Не всі властивості перелічені, тому всі вони не відображатимуться в циклі for / in.
А. Леві

Додавання до об'єкта-прототипу, подібного до цього, зламало jQuery для мене. Навіть коли я перейменований на клон2.
iPadDeveloper2011

3
@ iPadDeveloper2011 Код вище містив помилку, де він створив глобальну змінну під назвою 'i' '(для i в цьому)', а не '(для var i в цьому)'. У мене достатньо карми, щоб відредагувати її та виправити, так що я і зробив.
mikemaccana

1
@Calvin: для цього слід створити властивість, що не перелічується, інакше 'clone' з’явиться в циклі 'for'.
mikemaccana

2
чому це також не var copiedObj = Object.create(obj);чудовий спосіб?
Dan P.

19

У ES-6 ви можете просто використовувати Object.assign (...). Наприклад:

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);

Хороший посилання тут: https://googlechrome.github.io/samples/object-assign-es6/


12
Це не глибоко клонує предмет.
серпня

Це завдання, а не копія. clone.Title = "просто клон" означає, що obj.Title = "просто клон".
HoldOffHunger

@HoldOffHunger Ви помиляєтесь. Перевірте це в консолі JS браузера ( let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); clone.title = "Whazzup";)
згортання

@collapsar: Це саме те, що я перевірив, тоді console.log (людина) буде "Whazzup", а не "Тор Одінсон". Дивіться коментар Августа.
HoldOffHunger

1
@HoldOffHunger Не трапляється в Chrome 60.0.3112.113, ні в Edge 14.14393; Коментар Августа не застосовується, оскільки значення примітивних objвластивостей Росії справді клоновані. Значення властивостей, які є самими Об'єктами, не будуть клоновані.
згортання

18

В ECMAScript 2018

let objClone = { ...obj };

Майте на увазі, що вкладені об'єкти все ще копіюються як посилання.


1
Дякуємо за підказку, що вкладені об’єкти як і раніше копіюються як еталон! Я мало не з глузду зіткнувся з налагодженням свого коду, тому що я змінив вкладені властивості на "клон", але оригінал змінився.
Бенні Нойгебауер

Це ES2016, а не 2018 рік, і цю відповідь було дано на два роки раніше .
Дан Даскалеску

то що робити, якщо я хочу також копію вкладеної власності
Sunil Garg

1
@SunilGarg Щоб скопіювати вкладене майно, ви також можете використовувати const objDeepClone = JSON.parse(JSON.stringify(obj));
Pavan Garre

16

Використання Лодаша:

var y = _.clone(x, true);

5
OMG було б божевільно винаходити клонування. Це єдина розумна відповідь.
Ден Росс

5
Я вважаю за краще, _.cloneDeep(x)оскільки це по суті те саме, що і вище, але читає краще.
garbanzio

14

Зацікавлені в клонуванні простих об’єктів:

JSON.parse(JSON.stringify(json_original));

Джерело: Як скопіювати об’єкт JavaScript у нову змінну НЕ за посиланням?


Дуже приємно - просто.
Метт H

@MattH: цю відповідь було дано вже у 2012 році . Ви це бачили? Мухаммед, чи перевіряли ви наявні відповіді, перш ніж дублювати одну з них?
Дан Даскалеску

ну це один із способів. ty ніколи про це не думав
1-14x0r

13

Ви можете клонувати об’єкт і видалити будь-яку посилання з попереднього за допомогою одного рядка коду. Просто робіть:

var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references

obj2.text = 'moo2'; // Only updates obj2's text property

console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}

Для браузерів / двигунів, які наразі не підтримують Object.create, ви можете використовувати цю поліфункцію:

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}

1
+1 Object.create(...)здається, безумовно, шлях.
Рене Ніффенеггер

Ідеальна відповідь. Можливо, ви могли б додати пояснення Object.hasOwnProperty? Таким чином люди знають, як запобігти пошуку прототипу.
froginvasion

Добре працює, але в яких браузерах працює поліфайл?
Ian Lunn

11
Це створює obj2 за допомогою obj1 як його прототипу. Це працює лише тому, що ви textзатінюєте члена в obj2. Ви не створюєте копію, просто відклавшись до ланцюга прототипу, коли учасника не знайдено в obj2.
Нік Десольньє

2
Це НЕ створює його "без посилань", воно просто переміщує посилання на прототип. Це все ще довідка. Якщо властивість зміниться в оригіналі, так буде і властивість прототипу в "клоні". Це зовсім не клон.
Jimbo Jonny

13

Нова відповідь на старе запитання! Якщо у вас є задоволення від використання ECMAScript 2016 (ES6) із синтаксисом Spread , це просто.

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

Це забезпечує чистий метод для дрібної копії об'єкта. Створення глибокої копії, що означає зробити нову копію кожного значення у кожному рекурсивно вкладеному об'єкті, потребує більш важких рішень вище.

JavaScript постійно розвивається.


2
він не працює, коли у вас визначені функції на об'єктах
Петро Марек

наскільки я бачу, що оператор розповсюдження працює лише з ітерабелями - пише developer.mozilla.org : var obj = {'key1': 'value1'}; var array = [...obj]; // TypeError: obj is not iterable
Олег

@Oleh, то використовуйте `{... obj} замість [... obj];`
маніякальна гаутама

@manikantgautam Я раніше використовував Object.assign (), але тепер дійсно синтаксис розповсюдження об'єктів підтримується в останніх Chrome, Firefox (досі немає в Edge та Safari). Його пропозиція ECMAScript ... але Babel все-таки підтримує її, наскільки я бачу, тому, ймовірно, її безпечно використовувати.
Олег

12
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

Рішення ES6, якщо ви хочете (дрібно) клонувати екземпляр класу, а не лише об'єкт властивості.


Чим це відрізняється від let cloned = Object.assign({}, obj)?
ceztko

10

Я думаю, що тут є проста і працездатна відповідь. У глибокому копіюванні є дві проблеми:

  1. Зберігайте властивості незалежні один від одного.
  2. І тримати методи живими на клонованому об’єкті.

Тому я думаю, що одним простим рішенням буде спершу серіалізувати та десеріалізувати, а потім виконати призначення на ньому для копіювання функцій.

let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);

Хоча на це питання є багато відповідей, я сподіваюся, що і цей допомагає.


Хоча якщо мені дозволяється імпортувати лодаш, я вважаю за краще використовувати лодаш cloneDeep.
ConductedClever

2
Я використовую JSON.parse (JSON.stringify (джерело)). Завжди працюю.
Міша

2
@Misha, таким чином ви пропустите функції. Термін "працює" має багато значень.
ПроведенийКлевер

І майте на увазі, що, як я вже згадував, копіюються лише функції першого шару. Отже, якщо у нас є об’єкти всередині один одного, то єдиний спосіб - це копіювання поля на поле рекурсивно.
ПроведеноКлевер

9

Для глибокої копії та клонування JSON.stringify потім JSON.розділіть об'єкт:

obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}

досить розумний ... якісь недоліки цього підходу?
Алекс

8

Це адаптація коду А. Леві для обробки клонування функцій та кількох / циклічних посилань - що це означає, що якщо два властивості у клонованому дереві є посиланнями на один і той же об’єкт, дерево клонованих об'єктів матиме ці властивості вказують на один і той же клон посилається об'єкта. Це також вирішує випадок циклічних залежностей, які, якщо їх не обробити, призводять до нескінченного циклу. Складність алгоритму становить O (n)

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;

        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       


        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    var cloneObj = cloneRecursive(obj);



    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

Кілька швидких тестів

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));

1
Станом на вересень 2016 року, це єдине правильне рішення питання.
DomQ

6

Я просто хотів додати до всіх Object.createрішень у цій публікації, що це не працює бажаним чином з nodejs.

У Firefox результат

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

є

{test:"test"}.

У nodejs це так

{}

Це прототипічне успадкування, а не клонування.
d13

1
@ d13, поки ваш аргумент справедливий, зауважте, що в JavaScript немає стандартизованого способу клонування об’єкта. Це прототипічне успадкування, але воно може бути використане як клони, але якщо ви розумієте поняття.
froginvasion

@froginvasion. Єдина проблема використання Object.create полягає в тому, що вкладені об'єкти та масиви є лише посиланнями вказівників на вкладені об'єкти та масиви прототипу. jsbin.com/EKivInO/2/edit?js,console . Технічно "клонований" об'єкт повинен мати свої унікальні властивості, які не мають загальних посилань на властивості інших об'єктів.
d13

@ d13 Гаразд, зараз я бачу вашу думку. Але я мав на увазі те, що занадто багато людей відчужені концепцією прототипічного успадкування, і мені не вдалося дізнатися, як це працює. Якщо я не помиляюся, ваш приклад можна виправити, просто зателефонувавши, Object.hasOwnPropertyщоб перевірити, чи є у вас масив чи ні. Так, це додає додаткової складності для боротьби з прототиповим успадкуванням.
froginvasion

6
function clone(src, deep) {

    var toString = Object.prototype.toString;
    if(!src && typeof src != "object"){
        //any non-object ( Boolean, String, Number ), null, undefined, NaN
        return src;
    }

    //Honor native/custom clone methods
    if(src.clone && toString.call(src.clone) == "[object Function]"){
        return src.clone(deep);
    }

    //DOM Elements
    if(src.nodeType && toString.call(src.cloneNode) == "[object Function]"){
        return src.cloneNode(deep);
    }

    //Date
    if(toString.call(src) == "[object Date]"){
        return new Date(src.getTime());
    }

    //RegExp
    if(toString.call(src) == "[object RegExp]"){
        return new RegExp(src);
    }

    //Function
    if(toString.call(src) == "[object Function]"){
        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });

    }

    var ret, index;
    //Array
    if(toString.call(src) == "[object Array]"){
        //[].slice(0) would soft clone
        ret = src.slice();
        if(deep){
            index = ret.length;
            while(index--){
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }

    return ret;
};

2
if(!src && typeof src != "object"){. Я думаю , що має бути ||НЕ &&.
MikeM

6

Оскільки mindeavor заявив, що об'єкт, який підлягає клонуванню, є об'єктом, побудованим в буквальному сенсі, рішенням може бути просто генерування об'єкта кілька разів, а не клонування примірника об'єкта:

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();

6

Я написав власну реалізацію. Не впевнений, чи вважається це кращим рішенням:

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

Далі йде реалізація:

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}

не працює на мій об’єкт, хоча мій випадок трохи складний.
Саджук
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.