jQuery.extend () и Object.assign fail [duplicate]

Я получил решение.

Скорее, чтобы переопределить экран входящих вызовов, сделайте ниже двух вещей. который позволит вам получить доступ к кнопке «Принять и отклонить», а также отобразить экран над экраном вашего входящего вызова.

(1) Сделать один класс приемника:

public class MyPhoneReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
    Bundle extras = intent.getExtras();
    if (extras != null) 
    {
        String state = extras.getString(TelephonyManager.EXTRA_STATE);
        if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) 
            String phoneNumber = extras.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);

        Intent i = new Intent(context, IncomingCallActivity.class);
        i.putExtras(intent);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(i);
    }
    }
}

(2 ) ваша активность xml выглядит следующим образом:

RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="top"
android:gravity="top"
android:orientation="vertical"
android:windowAnimationStyle="@android:style/Animation.Translucent"
android:windowBackground="@android:color/transparent"
android:windowIsTranslucent="true"

(3) Сделайте прозрачность макета вашей деятельности (которая выйдет выше экрана вызова), напишите ниже код в манифесте

<activity android:name=".IncomingCallActivity" 
         android:theme="@android:style/Theme.Translucent">
</activity>

( 4) В манифесте добавьте свой широколистный приемник

<receiver android:name="MyPhoneReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" >
            </action>
        </intent-filter>
</receiver>

(5), добавьте ниже код в oncreate () из IncomingCallActivity

getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);

Приветствия!

Дайте мне знать, если вы столкнулись с какой-либо проблемой!

4686
задан 26 revs, 21 users 25% 22 March 2017 в 17:17
поделиться

31 ответ

Примечание. Это ответ на другой ответ, а не правильный ответ на этот вопрос. Если вы хотите быстро клонировать объекты, пожалуйста, следуйте советам Корбана в их ответе на этот вопрос.


Хочу отметить, что .clone() в jQuery только клонирует элементы DOM. Для клонирования объектов JavaScript вы бы сделали:

// Shallow copy
var newObject = jQuery.extend({}, oldObject);

// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);

Более подробную информацию можно найти в документации jQuery .

Также хочу отметить что глубокая копия на самом деле намного умнее, чем показано выше, - она ​​способна избежать многих ловушек (например, пытается глубоко расширить элемент DOM). Он часто используется в ядре jQuery и в плагинах.

4077
ответ дан 11 revs, 8 users 39% 15 August 2018 в 22:47
поделиться
  • 1
    Для тех, кто этого не понимал, ответ Джона Ресига был, вероятно, предназначен как своего рода ответ / разъяснение для ответа Конроя вместо прямого ответа на вопрос. – S. Kirby 2 September 2012 в 21:11
  • 2
    @ThiefMaster github.com/jquery/jquery/blob/master/src/core.js в строке 276 (есть немного кода, который делает что-то еще, кроме кода для "как это сделать в JS "есть): – Rune FS 27 March 2013 в 10:16
  • 3
    Вот JS-код, стоящий за глубокой копией jQuery, для всех, кого это интересует: github.com/jquery/jquery/blob/master/src/core.js#L265-327 – Alex W 11 April 2013 в 15:24
  • 4
    Ого! Просто чтобы быть супер-ясным: не знаю, почему этот ответ был выбран в качестве правильного ответа, это был ответ на ответы, приведенные ниже: stackoverflow.com/a/122190/6524 (который рекомендовал .clone(), который не является правильным кодом для использования в этом контексте). К сожалению, этот вопрос прошел через столько изменений, что первоначальная дискуссия уже не очевидна! Пожалуйста, просто следуйте советам Корбана и напишите цикл или скопируйте свойства непосредственно новому объекту, если вам нужна скорость. Или испытайте это для себя! – John Resig 21 January 2014 в 05:37
  • 5
    Как это сделать без использования jQuery? – Awesomeness01 30 April 2015 в 02:22

Cloning Объект всегда был предметом беспокойства в JS, но все это было до ES6, я перечисляю различные способы копирования объекта в JavaScript ниже, представьте, что у вас есть Объект ниже и вы хотите иметь глубокую копию что:

var obj = {a:1, b:2, c:3, d:4};

Существует несколько способов копирования этого объекта без изменения источника:

1) ES5 +, используя простую функцию для копирования:

function deepCopyObj(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }
    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 this object.");
}

2) ES5 +, используя JSON.parse и JSON.stringify.

var  deepCopyObj = JSON.parse(JSON.stringify(obj));

3) AngularJs:

var  deepCopyObj = angular.copy(obj);

4) jQuery:

var deepCopyObj = jQuery.extend(true, {}, obj);

5) UnderscoreJs & amp; Loadash:

var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy

Надеюсь, что эта помощь ...

44
ответ дан 11 revs, 2 users 92% 15 August 2018 в 22:47
поделиться
  • 1
    clone in underscore не является глубоким клоном в текущей версии – Rogelio 3 April 2017 в 16:19
  • 2
    Благодарю. yes as new doc для Underscore ... clone_.clone (object) Создайте клонированный объект с неполным копированием предоставленного простого объекта. Любые вложенные объекты или массивы будут скопированы по ссылке, а не дублированы. _.clone ({name: 'moe'}); = & GT; {name: 'moe'}; – Alireza 4 April 2017 в 01:00
  • 3
    Object.assign делает не глубокую копию. Пример: var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d". Если это была глубокая копия, y.a.b все равно будет c, но теперь она d. – kba 10 May 2017 в 15:52
  • 4
    Object.assign () только клонирует первый уровень свойств! – haemse 21 July 2017 в 10:13
  • 5
    Что такое функция cloneSO ()? – pastorello 7 August 2017 в 16:55

Глубокая копия по производительности: ранжировано от лучшего к худшему

  • Повторное назначение «=» (массивы строк, только массивы чисел)
  • Slice (строковые массивы, массивы чисел - только)
  • Конкатенация (только строковые массивы, только числовые массивы)
  • Пользовательская функция: для цикла или рекурсивной копии
  • jQuery's $ .extend
  • JSON.parse (только массивы строк, массивы чисел, только массивы объектов)
  • Underscore.js _.clone (массивы строк, только массивы чисел)
  • Lo-Dash's _.cloneDeep

Глубоко скопируйте массив строк или чисел (один уровень - без указателей):

Когда массив содержит числа и строки - такие функции, как .slice (), .concat (), .splice (), оператор присваивания «=» и функция клона Underscore.js; будет делать глубокую копию элементов массива.

Если переназначение имеет самую высокую производительность:

var arr1 = ['a', 'b', 'c'];
var arr2 = arr1;
arr1 = ['a', 'b', 'c'];

И .slice () имеет лучшую производительность, чем .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3

var arr1 = ['a', 'b', 'c'];  // Becomes arr1 = ['a', 'b', 'c']
var arr2a = arr1.slice(0);   // Becomes arr2a = ['a', 'b', 'c'] - deep copy
var arr2b = arr1.concat();   // Becomes arr2b = ['a', 'b', 'c'] - deep copy

Глубоко копировать массив объектов (два или более уровней - указатели ссылок):

var arr1 = [{object:'a'}, {object:'b'}];

Напишите пользовательскую функцию (имеет более высокую производительность, чем $ .extend () или JSON.parse):

function copy(o) {
   var out, v, key;
   out = Array.isArray(o) ? [] : {};
   for (key in o) {
       v = o[key];
       out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
   }
   return out;
}

copy(arr1);

Используйте сторонние служебные функции:

$.extend(true, [], arr1); // Jquery Extend
JSON.parse(arr1);
_.cloneDeep(arr1); // Lo-dash

Там, где $ .extend jQuery имеет лучшую производительность:

64
ответ дан 12 revs, 2 users 93% 15 August 2018 в 22:47
поделиться
  • 1
    Я тестировал несколько, а _.extend ({}, (obj)) был BY FAR самым быстрым: 20 раз быстрее, чем JSON.parse и на 60% быстрее, чем Object.assign, например. Он полностью копирует все под-объекты. – Nico 9 May 2016 в 19:56
  • 2
    @NicoDurand - Ваши тесты производительности онлайн? – tfmontague 17 May 2016 в 13:36
  • 3
    Все ваши примеры неглубокие, один уровень. Это не очень хороший ответ. Вопрос касался глубокого клонирования, то есть по меньшей мере двух уровней. – Karl Morrison 21 April 2017 в 10:02
  • 4
    Глубокая копия - это когда объект копируется целиком без использования указателей ссылок на другие объекты. Методы в разделе «Глубоко копировать массив объектов», таких как jQuery.extend () и пользовательская функция (которая является рекурсивной), копируют объекты с «по меньшей мере двумя уровнями». Таким образом, не все примеры являются «одним уровнем». копии. – tfmontague 21 April 2017 в 15:42
  • 5
    Мне нравится ваша пользовательская функция копирования, но вы должны исключать нулевые значения, иначе все нулевые значения преобразуются в объекты, то есть: out[key] = (typeof v === "object" && v !== null) ? copy(v) : v; – josi 1 February 2018 в 17:53

Ознакомиться с этим эталоном: http://jsben.ch/#/bWfk9

В моих предыдущих тестах, где скорость была главной проблемой, я нашел

JSON.parse(JSON.stringify(obj))

- самый быстрый способ глубокого клонирования объекта (он выдает jQuery.extend с установленным значением глубокого флага на 10-20%).

jQuery.extend довольно быстро, когда для флага глубокого значения установлено значение false (мелкий клон). Это хороший вариант, поскольку он включает некоторую дополнительную логику для проверки типов и не копирует неопределенные свойства и т. Д., Но это также немного замедлит вас.

Если вам известна структура объекты, которые вы пытаетесь клонировать или можете избежать глубоких вложенных массивов, вы можете написать простой цикл for (var i in obj), чтобы клонировать ваш объект при проверке hasOwnProperty, и он будет намного быстрее, чем jQuery.

Наконец, если вы пытаясь клонировать известную структуру объекта в горячем цикле, вы можете получить МНОГО БОЛЬШЕ БОЛЬШЕ ПРОИЗВОДИТЕЛЬНОСТИ, просто вставив процедуру клонирования и вручную создав объект.

Механизмы слежения за JavaScript присасываются при оптимизации циклов for..in и проверка hasOwnProperty также замедлит вас. Ручной клон, когда скорость является абсолютной необходимостью.

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

Остерегайтесь использования метода JSON.parse(JSON.stringify(obj)) на объектах Date - JSON.stringify(new Date()) возвращает строковое представление даты в формате ISO, которое JSON.parse() не обращается обратно к объекту Date. См. этот ответ для получения дополнительной информации .

Кроме того, обратите внимание, что в Chrome 65, по крайней мере, нативный клонирование не подходит. Согласно этот JSPerf , выполняющий собственное клонирование, создавая новую функцию, почти на 800 раз медленнее, чем использование JSON.stringify, которое невероятно быстро распространяется по всем направлениям.

1888
ответ дан 13 revs, 10 users 68% 15 August 2018 в 22:47
поделиться
  • 1
    @trysis Object.create не клонирует объект, использует объект-прототип ... jsfiddle.net/rahpuser/yufzc1jt/2 – rahpuser 25 November 2014 в 22:37
  • 2
    Этот метод также удалит keys из вашего object, который имеет functions в качестве своих значений, потому что JSON не поддерживает функции. – Karlen Kishmiryan 29 May 2015 в 09:52
  • 3
    Также имейте в виду, что использование JSON.parse(JSON.stringify(obj)) в Date Objects также преобразует дату в UTC в строковое представление в формате ISO8601 . – dnlgmzddr 30 July 2015 в 21:37
  • 4
    Подход JSON также дросселирует по круговым ссылкам. – rich remer 13 February 2016 в 06:25
  • 5
    @velop, Object.assign ({}, objToClone) кажется, что он делает мелкий клон, хотя - используя его во время игры в консоли инструментов dev, клон объекта все еще указывает на ссылку клонированного объекта. Поэтому я не думаю, что это действительно применимо здесь. – Garrett Simpson 3 November 2016 в 19:00

Я обычно использую var newObj = JSON.parse( JSON.stringify(oldObje) );, но вот более правильный способ:

var o = {};

var oo = Object.create(o);

(o === oo); // => false

Смотреть устаревшие браузеры!

91
ответ дан 2 revs 15 August 2018 в 22:47
поделиться
  • 1
    Этот ответ не очень уместен, потому что вопрос: данный экземпляр b как создать копию c WHILE, не зная о фабрике или не желая использовать завод a. Причина, по которой вы не можете использовать фабрику, заключается в том, что после создания экземпляра b могут быть инициализированы дополнительные данные (например, пользовательский ввод). – Noel Abrahams 8 May 2012 в 10:57
  • 2
    Это правда, что на самом деле это не ответ на этот вопрос, но я думаю, что это важно, потому что это ответ на вопрос, который, как я подозреваю, многие из пришедших сюда людей действительно хотят спросить. – Semicolon 7 March 2013 в 01:31
  • 3
    Второй способ требует прототипа, я предпочитаю первый способ, даже если он не самый лучший в производительности, потому что вы можете использовать его с большим количеством браузеров и с Node JS. – Hola Soy Edu Feliz Navidad 3 May 2014 в 11:03
  • 4
    Извините, ребята, я действительно не понимаю, почему так много upvotes. Клонирование объекта - довольно понятное понятие, вы делаете объект из объекта ANOTHER, и он не имеет ничего общего с созданием нового шаблона фабрики. – opensas 18 August 2014 в 13:51
  • 5
    как насчет var obj = {} и obj.a = obj – neaumusic 5 May 2015 в 22:40
  • 6
    Я не понимаю эту функцию. Предположим, что from.constructor - Date. Как будет проведен третий тест if, когда 2-й тест if будет успешным и, заставить функцию вернуться (поскольку Date != Object && Date != Array)? – Adam McKee 1 September 2015 в 02:10
  • 7
    Это круто и все, но предположим, что o обладает свойством a. Теперь делает oo.hasOwnProperty ('a')? – user420667 29 March 2016 в 23:54
  • 8
    No-o по существу добавляется как прототип оо. Вероятно, это не будет желательным поведением, поэтому 99,9% методов serialize (), которые я пишу, используют упомянутый выше подход JSON. В основном я использую JSON, и при использовании Object.create возникают другие оговорки. – Cody 30 March 2016 в 02:30
  • 9
    @AdamMcKee Поскольку передача аргументов javascript и назначение переменной сложны . Этот подход отлично работает, включая даты (которые действительно обрабатываются вторым тестом) - скрипка для тестирования здесь: jsfiddle.net/zqv9q9c6 . – brichins 14 June 2016 в 20:39
  • 10
    * Я имел в виду "назначение параметров" (внутри тела функции), а не «присвоение переменной». Хотя это помогает понять и тщательно. – brichins 15 June 2016 в 16:42
  • 11
    Хотя это работает для предопределенных объектов, «клонирование» таким образом, не будет распознавать новые свойства, добавленные к исходному объекту. Если вы создадите a, добавьте новое свойство в a, а затем создайте b. b не будет нового свойства. По существу фабричная модель неизменна для новых свойств. Это не парадигматически клонирует. См .: jsfiddle.net/jzumbrun/42xejnbx – Jon 3 September 2016 в 13:59
  • 12
    Я думаю, что это хороший совет, как правило, поскольку вместо использования const defaultFoo = { a: { b: 123 } }; вы можете пойти const defaultFoo = () => ({ a: { b: 123 } };, и ваша проблема решена. Однако на самом деле это не ответ на вопрос. Это могло бы иметь больше смысла в качестве комментария к вопросу, а не полного ответа. – Josh from Qaribou 6 February 2017 в 15:25
  • 13
    @NickSweeting: Попробуйте - возможно, это сработает. Если нет - исправьте и обновите ответ. Вот как это работает в сообществе :) – Kamarey 10 July 2017 в 07:20
  • 14
    Нет, смотрите этот код! Object.create не требуется создание копии объекта, вместо этого он использует старый объект в качестве прототипа для клона – 16kb 22 June 2018 в 01:07

Крокфорд предлагает (и я предпочитаю) использовать эту функцию:

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

var newObject = object(oldObject);

Он краток, работает как ожидалось, и вам не нужна библиотека.


EDIT :

Это полиполк для Object.create, поэтому вы также можете использовать это.

var newObject = Object.create(oldObject);

ПРИМЕЧАНИЕ. Если вы используете часть этого, у вас могут быть проблемы с некоторой итерацией кто использует hasOwnProperty. Потому что create создает новый пустой объект, который наследует oldObject. Но это все еще полезно и практично для клонирования объектов.

Например, если oldObject.a = 5;

newObject.a; // is 5

, но:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false
20
ответ дан 2 revs, 2 users 50% 15 August 2018 в 22:47
поделиться
  • 1
    исправьте меня, если я ошибаюсь, но разве это не новорожденная функция Крокфорда для прототипного наследования? Как это относится к клону? – Alex Nolasco 6 October 2010 в 16:17
  • 2
    Да, я боялся этой дискуссии: какова практическая разница между клонированием, копированием и прототипным наследованием, когда вы должны использовать каждую и какие функции на этой странице на самом деле делают? Я нашел эту страницу SO путем googling & quot; javascript copy object & quot ;. То, что я действительно искал, было функцией выше, поэтому я вернулся, чтобы поделиться. Я думаю, что искатель тоже искал это. – Chris Broski 6 October 2010 в 20:51
  • 3
    Разница между клоном / копией и наследованием заключается в том, что - используя ваш пример, когда я изменяю свойство oldObject, свойство также изменяется в newObject. Если вы сделаете копию, вы можете делать то, что хотите, с помощью oldObject без изменения newObject. – Ridcully 6 December 2010 в 14:13
  • 4
    Это приведет к поломке проверки hasOwnProperty, так что это довольно хакерский способ клонирования объекта и даст неожиданные результаты. – Corban Brook 16 March 2011 в 19:17
  • 5
    var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; }; ... davidshariff.com/blog/javascript-inheritance-patterns – Cody 24 April 2014 в 19:32

Lodash имеет хороший метод _. cloneDeep (value) :

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
19
ответ дан 2 revs, 2 users 67% 15 August 2018 в 22:47
поделиться
  • 1
    Я сторонник удаления этого и всех других ответов, которые являются лишь однострочными ссылками на метод .clone(...) библиотеки коммунальных услуг. Каждая основная библиотека будет иметь их, и повторяющиеся краткие не-подробные ответы не будут полезны большинству посетителей, которые не будут использовать эту конкретную библиотеку. – Jeremy 7 April 2016 в 17:23
  • 2
    Более простой способ - использовать _.merge({}, objA). Если только lodash не мутировал объекты в первую очередь, тогда функция clone не понадобилась. – Rebs 27 February 2017 в 01:48
  • 3
    Google ищет клонирование объектов JS. Я использую Lodash, поэтому этот ответ имеет отношение ко мне. Позволяет не идти на все «википедию». на ответы пожалуйста. – Rebs 27 February 2017 в 01:49
  • 4
    В узле 9 JSON.parse (JSON.stringify (arrayOfAbout5KFlatObjects)) намного быстрее, чем _.deepClone (arrayOfAbout5KFlatObjects). – Dan Dascalescu 31 December 2017 в 14:07

Только когда вы можете использовать ECMAScript 6 или транспилеры .

Особенности:

  • Не запускается геттер / сеттер при копировании.
  • Сохраняет геттер / сеттер.
  • Сохраняет информацию о прототипе.
  • Работает как с объектно-литералами, так и с функционалом OO .

Код:

function clone(target, source){

    for(let key in source){

        // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter.
        let descriptor = Object.getOwnPropertyDescriptor(source, key);
        if(descriptor.value instanceof String){
            target[key] = new String(descriptor.value);
        }
        else if(descriptor.value instanceof Array){
            target[key] = clone([], descriptor.value);
        }
        else if(descriptor.value instanceof Object){
            let prototype = Reflect.getPrototypeOf(descriptor.value);
            let cloneObject = clone({}, descriptor.value);
            Reflect.setPrototypeOf(cloneObject, prototype);
            target[key] = cloneObject;
        }
        else {
            Object.defineProperty(target, key, descriptor);
        }
    }
    let prototype = Reflect.getPrototypeOf(source);
    Reflect.setPrototypeOf(target, prototype);
    return target;
}
6
ответ дан 2 revs, 2 users 69% 15 August 2018 в 22:47
поделиться
// obj target object, vals source object
var setVals = function (obj, vals) {
    if (obj && vals) {
        for (var x in vals) {
            if (vals.hasOwnProperty(x)) {
                if (obj[x] && typeof vals[x] === 'object') {
                    obj[x] = setVals(obj[x], vals[x]);
                } else {
                    obj[x] = vals[x];
                }
            }
        }
    }
    return obj;
};
9
ответ дан 2 revs, 2 users 70% 15 August 2018 в 22:47
поделиться

У меня есть два хороших ответа в зависимости от того, должна ли ваша цель клонировать «простой старый объект JavaScript» или нет.

Предположим также, что вы намерены создать полный клон без ссылок на прототипы назад к исходному объекту. Если вас не интересует полный клон, вы можете использовать многие из подпрограмм Object.clone (), предоставленные в некоторых других ответах (шаблон Крокфорда).

Для простых старых объектов JavaScript надежный и надежный способ клонирования объекта в современных средах выполнения довольно просто:

var clone = JSON.parse(JSON.stringify(obj));

Обратите внимание, что исходный объект должен быть чистым объектом JSON. Это означает, что все его вложенные свойства должны быть скалярами (например, логическими, строковыми, массивными, объектными и т. Д.). Любые функции или специальные объекты, такие как RegExp или Date, не будут клонированы.

Является ли он эффективным? Черт возьми. Мы пробовали все виды методов клонирования, и это работает лучше всего. Я уверен, что какой-нибудь ниндзя может вызвать более быстрый метод. Но я подозреваю, что мы говорим о предельных выигрышах.

Этот подход просто прост и прост в реализации. Включите его в функцию удобства, и если вам действительно нужно выжать некоторый выигрыш, идите позже.

Теперь, для не-простых объектов JavaScript, нет простого ответа. На самом деле не может быть из-за динамического характера функций JavaScript и состояния внутреннего объекта. Глубокое клонирование структуры JSON с функциями внутри требует, чтобы вы воссоздали эти функции и их внутренний контекст. И JavaScript просто не имеет стандартного способа сделать это.

Правильный способ сделать это еще раз - это метод удобства, который вы декларируете и повторно используете в своем коде. Метод удобства может быть наделен некоторым пониманием ваших собственных объектов, чтобы вы могли правильно воссоздать график в новом объекте.

Мы пишем наш собственный, но лучший общий подход. см. здесь:

http://davidwalsh.name/javascript-clone

Это правильная идея. Автор (Дэвид Уолш) прокомментировал клонирование обобщенных функций.

Основная идея заключается в том, что вам нужно специально обработать инстанцирование ваших функций (или прототипных классов, если можно так выразиться) тип основы. Здесь он предоставил несколько примеров для RegExp и Date.

Этот код не только краткий, но и очень читабельный. Это довольно легко расширить.

Это эффективно? Черт возьми. Учитывая, что цель состоит в том, чтобы создать настоящий клон с глубокой копией, вам придется ходить по членам графа исходного объекта. С помощью этого подхода вы можете точно настроить, какие дочерние элементы обрабатывать и как вручную обрабатывать настраиваемые типы.

Итак, вы идете. Два подхода. На мой взгляд, обе эффективны.

13
ответ дан 2 revs, 2 users 81% 15 August 2018 в 22:47
поделиться

Если вы используете его, библиотека Underscore.js имеет метод clone .

var newObject = _.clone(oldObject);
45
ответ дан 2 revs, 2 users 83% 15 August 2018 в 22:47
поделиться
  • 1
    lodash имеет метод cloneDeep, он также поддерживает другой параметр для клонирования, чтобы сделать его глубоким: lodash.com/docs#clone и lodash.com/docs#cloneDeep – opensas 2 March 2013 в 19:14
  • 2
    @opensas согласился. Lodash обычно превосходит знак подчеркивания – nha 31 July 2014 в 13:12
  • 3
    Я сторонник удаления этого и всех других ответов, которые являются лишь однострочными ссылками на метод .clone(...) библиотеки коммунальных услуг. Каждая основная библиотека будет иметь их, и повторяющиеся краткие не-подробные ответы не будут полезны большинству посетителей, которые не будут использовать эту конкретную библиотеку. – Jeremy 7 April 2016 в 17:25

Существует библиотека (так называемый «клон») , что делает это довольно хорошо. Он обеспечивает наиболее полное рекурсивное клонирование / копирование произвольных объектов, о которых я знаю. Он также поддерживает циклические ссылки, которые еще не покрыты другими ответами.

Вы можете найти его на npm . Его можно использовать как для браузера, так и для Node.js.

Вот пример того, как его использовать:

Установите его с помощью

npm install clone

или упакуйте его с помощью Ender .

ender build clone [...]

Вы также можете загрузить исходный код вручную.

Затем вы можете использовать его в своем исходном коде.

var clone = require('clone');

var a = { foo: { bar: 'baz' } };  // inital value of a
var b = clone(a);                 // clone a -> b
a.foo.bar = 'foo';                // change a

console.log(a);                   // { foo: { bar: 'foo' } }
console.log(b);                   // { foo: { bar: 'baz' } }

(Отказ от ответственности: я являюсь автором библиотеки.)

47
ответ дан 3 revs 15 August 2018 в 22:47
поделиться
  • 1
    npm clone был бесценен для меня для клонирования произвольно вложенных объектов. Это правильный ответ. – Andy Ray 10 September 2015 в 03:21
  • 2
    Какова производительность вашей библиотеки по сравнению, скажем, JSON.parse(JSON.stringify(obj))? – pkyeck 1 December 2016 в 17:03
  • 3
    Вот библиотека , в которой указано, что есть более быстрые параметры. Не тестировали. – pvorb 1 December 2016 в 22:07

Просто потому, что я не видел AngularJS и думал, что люди захотят узнать ...

angular.copy также предоставляет метод глубоких копирующих объектов и массивов.

15
ответ дан 3 revs, 2 users 62% 15 August 2018 в 22:47
поделиться
  • 1
    или он может использоваться так же, как и расширение jQuery: angular.extend({},obj); – Galvani 21 September 2016 в 09:07
  • 2
    @Galvani: Следует отметить, что jQuery.extend и angular.extend являются неглубокими копиями. angular.copy - это глубокая копия. – Dan Atkinson 15 October 2016 в 18:41

Неглубокая копия с одним слоем ( 5-е издание ECMAScript ):

var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

И мелкая копия с одним слоем ( ECMAScript 6-е издание , 2015 ):

var origin = { foo : {} };
var copy = Object.assign({}, origin);

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
18
ответ дан 3 revs, 2 users 82% 15 August 2018 в 22:47
поделиться
  • 1
    Это может быть хорошо для простых объектов, но оно копирует только значения свойств. Он не затрагивает цепочку прототипов и, используя Object.keys, пропускает неперечислимые и унаследованные свойства. Кроме того, он теряет дескрипторы свойств, выполняя прямое назначение. – Matt Bierner 23 November 2013 в 19:51
  • 2
    Если вы также скопируете прототип, вам будут отсутствовать только неперечислимые и дескрипторы свойств, да? Довольно хорошо. :) – sam 8 June 2014 в 07:24
  • 3
    Производительность в стороне, это действительно удобный способ мелкой копии объекта. Я часто использую это для создания поддельных свойств отдыха в задаче деструктурирования в моих компонентах React. – mjohnsonengr 17 March 2016 в 14:56

До сих пор нет идеального оператора глубокого клонирования для объектов типа массива. Как видно из приведенного ниже кода, клон JQuery Джона Ресига превращает массивы с нечисловыми свойствами в объекты, которые не являются массивами, а клонирование Cloner от RegDwight снижает нечисловые свойства. Следующие тесты иллюстрируют эти моменты в нескольких браузерах:

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)
14
ответ дан 3 revs, 2 users 98% 15 August 2018 в 22:47
поделиться
  • 1
    как другие отмечали в комментариях к ответу Resig, если вы хотите клонировать объект типа типа, вы меняете {} на [] в вызове расширения, например jQuery.extend (true, [], obj) – Anentropic 3 March 2011 в 22:27
  • 2
    JSON.stringify не работает с functions – Vlada 12 March 2016 в 16:08

Предполагая, что у вас есть только переменные, а не какие-либо функции в вашем объекте, вы можете просто использовать:

var newObject = JSON.parse(JSON.stringify(oldObject));
404
ответ дан 3 revs, 3 users 63% 15 August 2018 в 22:47
поделиться
  • 1
    con такого подхода, как я только что нашел, - это если у вашего объекта есть какие-либо функции (у меня есть внутренние getters & amp; seters), тогда они теряются при стягивании. Если это все, что вам нужно, этот метод в порядке. – Markive 17 January 2013 в 13:00
  • 2
    @Jason. Причина, по которой этот метод медленнее, чем мелкое копирование (на глубоком объекте), заключается в том, что этот метод, по определению, имеет глубокие копии. Но поскольку JSON реализован в собственном коде (в большинстве браузеров), это будет значительно быстрее, чем использование любого другого решения для глубокого копирования на основе javascript, и может иногда быть быстрее, чем на основе javascript (см. jsperf.com/cloning-an-object/79 ). – MiJyn 4 July 2013 в 08:42
  • 3
    JSON.stringify({key: undefined}) //=> "{}" – Web_Designer 30 April 2014 в 07:24
  • 4
    этот метод уничтожит также все Date объекты, которые хранятся внутри объекта, преобразуя их в строчную форму. – fstab 21 August 2014 в 13:51
  • 5
    Он не сможет скопировать что-либо, что не является частью спецификации JSON ( json.org ) – cdmckay 9 February 2016 в 16:07

Это то, что я использую:

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(typeof(obj[i])=="object" && obj[i] != null)
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}
81
ответ дан 3 revs, 3 users 69% 15 August 2018 в 22:47
поделиться
  • 1
    Это не кажется правильным. cloneObject({ name: null }) = & gt; {"name":{}} – Niyaz 27 February 2013 в 16:36
  • 2
    Это связано с другой немой вещью в javascript typeof null > "object", но Object.keys(null) > TypeError: Requested keys of a value that is not an object. изменяет условие на if(typeof(obj[i])=="object" && obj[i]!=null) – Vitim.us 16 April 2013 в 17:42
  • 3
    Это назначит унаследованные перечисленные свойства obj непосредственно клону и предполагает, что obj - простой объект. – RobG 1 February 2016 в 23:58
  • 4
    Это также испортит массивы, которые преобразуются в объекты с помощью цифровых клавиш. – blade 3 November 2016 в 13:29
  • 5
    Не проблема, если вы не используете null. – Jorge Bucaran 1 March 2017 в 07:17

Следующие создаются два экземпляра одного и того же объекта. Я нашел его и использую его в настоящее время. Он прост и удобен.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));
30
ответ дан 3 revs, 3 users 71% 15 August 2018 в 22:47
поделиться
  • 1
    Что-то не так с этим ответом? Это более полезно как самостоятельное решение, но простое; но решение jQuery более популярно. Почему это? – ceremcem 24 September 2015 в 17:15
  • 2
    Да, дайте мне знать. Кажется, он работает по назначению, если где-то есть какие-то скрытые поломки, мне нужно использовать другое решение. – nathan rogers 25 September 2015 в 15:34
  • 3
    Для простого объекта это примерно в 6 раз медленнее в Chrome, чем данный ответ, и становится намного медленнее по мере роста сложности объекта. Он ужасно масштабируется и может быстро помешать вашему приложению. – tic 9 December 2015 в 00:10
  • 4
    Вам не нужны данные, просто понимание того, что происходит. Этот метод клонирования сериализует весь объект в строку, затем анализирует эту сериализацию строк для построения объекта. По сути, это будет намного медленнее, чем просто переустановка некоторой памяти (это то, что делают более сложные клоны). Но с учетом сказанного, для проектов малого и среднего размера (в зависимости от вашего определения «среднего размера»), который заботится, даже если он даже менее эффективен на 1000 раз? Если ваши объекты небольшие, и вы не клонируете их, то тонна 1000x практически ничего по-прежнему практически ничего. – machineghost 19 December 2016 в 21:20
  • 5
    Кроме того, этот метод теряет методы (или любые вещи, которые не разрешены в JSON), плюс - JSON.stringify преобразует объекты Date в строки, ... а не наоборот;) Пребывание от этого решения. – Mr MT 11 August 2017 в 22:11

Эффективный способ клонирования (не глубокого клонирования) объекта в одной строке кода

Метод Object.assign является частью стандарта ECMAScript 2015 (ES6) и делает именно то, что вам нужно.

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

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

Подробнее ...

Полипол для поддержки старых браузеров:

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;
    }
  });
}
135
ответ дан 3 revs, 3 users 95% 15 August 2018 в 22:47
поделиться
  • 1
    Это не рекурсивно копирует, поэтому на самом деле не предлагает решения проблемы клонирования объекта. – mwhite 8 March 2016 в 20:56
  • 2
    Этот метод работал, хотя я тестировал несколько, а _.extend ({}, (obj)) был быстрее FAR: на 20 раз быстрее, чем JSON.parse и на 60% быстрее, чем Object.assign, например. Он полностью копирует все под-объекты. – Nico 9 May 2016 в 19:57
  • 3
    @mwhite существует разница между клоном и глубоким клоном. Этот ответ действительно клонирует, но он не глубоко клонирован. – Meirion Hughes 8 June 2016 в 12:08
  • 4
    Оп спросил глубокий клон. это не делает глубокий клон. – user566245 31 July 2016 в 03:49
  • 5
    wow так много ответов Object.assign, даже не прочитав вопрос op ... – martin8768 29 December 2017 в 11:02

Вот комплексный метод clone (), который может клонировать любой объект JavaScript. Он обрабатывает почти все случаи:

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;
};
7
ответ дан 4 revs, 4 users 59% 15 August 2018 в 22:47
поделиться
  • 1
    Он преобразует примитивы в объекты-обертки, что не является хорошим решением в большинстве случаев. – Danubian Sailor 1 August 2014 в 10:58
  • 2
    @DanubianSailor - я не думаю, что это так ... кажется, что он возвращает примитивы сразу же с самого начала и, похоже, ничего не делает для них, что превратит их в объекты-оболочки, когда они будут возвращены. – Jimbo Jonny 2 February 2016 в 19:06
var clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (var i in this) {
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        }
        else
        {
            newObj[i] = this[i];
        }
    }
    return newObj;
}; 

Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});
56
ответ дан 5 revs, 4 users 31% 15 August 2018 в 22:47
поделиться

Вот версия ответа ConroyP выше, которая работает, даже если у конструктора требуются параметры:

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

function deepCopy(obj) {
    if(obj == null || typeof(obj) !== 'object'){
        return obj;
    }
    //make sure the returned object has the same prototype as the original
    var ret = object_create(obj.constructor.prototype);
    for(var key in obj){
        ret[key] = deepCopy(obj[key]);
    }
    return ret;
}

Эта функция также доступна в моей библиотеке simpleoo .

Редактировать:

Вот более надежная версия (благодаря Джастину МакКэндлесу, теперь это также поддерживает циклические ссылки):

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.push(src);
        _copiesVisited.push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var dest = object_create(proto);

    //add this object to the visited array
    _visited.push(src);
    _copiesVisited.push(dest);

    for (var key in src) {
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}
33
ответ дан 9 revs 15 August 2018 в 22:47
поделиться

Если не было встроенного, вы можете попробовать:

    function clone(obj) {
      if (obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

      if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
      else
        var temp = obj.constructor();

      for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
          obj['isActiveClone'] = null;
          temp[key] = clone(obj[key]);
          delete obj['isActiveClone'];
        }
      }

      return temp;
    }

275
ответ дан 9 revs, 7 users 36% 15 August 2018 в 22:47
поделиться
  • 1
    Решение JQuery будет работать для элементов DOM, но не только для любого объекта. Mootools имеет тот же предел. Имейте в виду, что у них был общий «клон» для всего объекта ... Рекурсивное решение должно работать на что угодно. Вероятно, это путь. – jschrab 23 September 2008 в 18:23
  • 2
    Эта функция ломается, если клонируемый объект имеет конструктор, который требует параметров. Кажется, мы можем изменить его на «var temp = new Object ()». и пусть он работает в каждом случае, нет? – Andrew Arnott 4 October 2009 в 23:06
  • 3
    Андрей, если вы измените его на var temp = new Object (), тогда ваш клон не будет иметь тот же прототип, что и исходный объект. Попробуйте использовать: 'var newProto = function () {}; newProto.prototype = obj.constructor; var temp = new newProto (); ' – limscoder 14 September 2011 в 16:53
  • 4
    Как и в ответе limscoder, см. Мой ответ ниже о том, как это сделать, не вызывая конструктор: stackoverflow.com/a/13333781/560114 – Matt Browne 11 November 2012 в 19:55
  • 5
    Для объектов, содержащих ссылки на подчасти (т. Е. Сети объектов), это не работает: если две ссылки указывают на один и тот же под-объект, копия содержит две разные копии. И если есть рекурсивные ссылки, функция никогда не прекратится (ну, по крайней мере, не так, как вы этого хотите :-) Для этих общих случаев вам нужно добавить словарь уже скопированных объектов и проверить, скопировали ли вы его ... Программирование сложное, когда вы используете простой язык – virtualnobi 11 November 2013 в 10:34

AngularJS

Хорошо, если вы используете угловой, вы тоже можете это сделать

var newObject = angular.copy(oldObject);
9
ответ дан azerafati 15 August 2018 в 22:47
поделиться

Структурированное клонирование

HTML5 определяет внутренний «структурированный» алгоритм клонирования , который может создавать глубокие клоны объектов. Он по-прежнему ограничен определенными встроенными типами, но в дополнение к нескольким типам, поддерживаемым JSON, он также поддерживает Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, редкие массивы, Typed Arrays , и, вероятно, больше в будущем. Он также сохраняет ссылки в клонированных данных, позволяя ему поддерживать циклические и рекурсивные структуры, которые могут вызывать ошибки для JSON.

Прямая поддержка в браузерах: скоро?

276
ответ дан Jeremy 15 August 2018 в 22:47
поделиться
  • 1
    @rynah Я просто просмотрел спецификацию снова, и вы правы: методы history.pushState() и history.replaceState() оба синхронно устанавливают history.state в структурированный клон их первого аргумента. Немного странно, но это работает. Я обновляю свой ответ. – Jeremy 6 May 2013 в 04:11
  • 2
    Это просто неправильно! Этот API не предназначен для использования таким образом. – Fardin 1 August 2014 в 00:34
  • 3
    Как парень, который реализовал pushState в Firefox, я чувствую странную смесь гордости и отвращения к этому взлому. Молодцы ребята. – Justin L. 14 August 2014 в 19:37
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }
22
ответ дан Mark Cidade 15 August 2018 в 22:47
поделиться
  • 1
    Проблема с методом, что если у вас есть подчиненные объекты в объекте, их ссылки будут клонированы, а не значения каждого субобъекта. – Kamarey 25 June 2009 в 08:46
  • 2
    просто сделайте его рекурсивным, чтобы суб объекты были клонированы глубоко. – fiatjaf 26 January 2013 в 00:38
  • 3
    просто любопытно ... не будет ли переменная клона иметь указатели на свойства исходного объекта? потому что, похоже, нет нового распределения памяти – Rupesh Patel 4 February 2013 в 11:42
  • 4
    Да. Это всего лишь мелкая копия, поэтому клон будет указывать на те же объекты, на которые указывает оригинальный объект. – Mark Cidade 4 February 2013 в 12:18

Это, как правило, не самое эффективное решение, но оно делает то, что мне нужно. Простые тестовые примеры ниже ...

function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's in the 'clones' parameter. Functions 
    // are included, but not cloned. Functions members are cloned.
    var new_obj,
        already_cloned,
        t = typeof obj,
        i = 0,
        l,
        pair; 

    clones = clones || [];

    if (obj === null) {
        return obj;
    }

    if (t === "object" || t === "function") {

        // check to see if we've already cloned obj
        for (i = 0, l = clones.length; i < l; i++) {
            pair = clones[i];
            if (pair[0] === obj) {
                already_cloned = pair[1];
                break;
            }
        }

        if (already_cloned) {
            return already_cloned; 
        } else {
            if (t === "object") { // create new object
                new_obj = new obj.constructor();
            } else { // Just use functions as is
                new_obj = obj;
            }

            clones.push([obj, new_obj]); // keep track of objects we've cloned

            for (key in obj) { // clone object members
                if (obj.hasOwnProperty(key)) {
                    new_obj[key] = clone(obj[key], clones);
                }
            }
        }
    }
    return new_obj || obj;
}

Тест циклического массива ...

a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true

Функциональный тест ...

f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false
11
ответ дан neatonk 15 August 2018 в 22:47
поделиться

Я не согласен с ответом с наибольшим количеством голосов здесь . Рекурсивный глубокий клон намного быстрее, чем упомянутый подход JSON.parse (JSON.stringify (obj)) .

И вот функция для быстрой справки:

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}
9
ответ дан prograhammer 15 August 2018 в 22:47
поделиться
  • 1
    Мне понравился этот подход, но он не обрабатывает даты должным образом; подумайте над добавлением чего-то вроде if(o instanceof Date) return new Date(o.valueOf()); после проверки на нуль ` – Luis 21 August 2017 в 22:53
  • 2
    Аварии на круговых ссылках. – Harry 18 March 2018 в 06:19

Я знаю, что это старый пост, но я подумал, что это может помочь кому-то, кто спотыкается.

Пока вы не назначаете объект ни к чему, он не поддерживает ссылка в памяти. Поэтому, чтобы создать объект, который вы хотите разделить между другими объектами, вам нужно создать такую ​​фабрику:

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);
91
ответ дан 2 revs 15 August 2018 в 22:47
поделиться
  • 1
    Этот ответ не очень уместен, потому что вопрос: данный экземпляр b как создать копию c WHILE, не зная о фабрике или не желая использовать завод a. Причина, по которой вы не можете использовать фабрику, заключается в том, что после создания экземпляра b могут быть инициализированы дополнительные данные (например, пользовательский ввод). – Noel Abrahams 8 May 2012 в 10:57
  • 2
    Это правда, что на самом деле это не ответ на этот вопрос, но я думаю, что это важно, потому что это ответ на вопрос, который, как я подозреваю, многие из пришедших сюда людей действительно хотят спросить. – Semicolon 7 March 2013 в 01:31
  • 3
    Извините, ребята, я действительно не понимаю, почему так много upvotes. Клонирование объекта - довольно понятное понятие, вы делаете объект из объекта ANOTHER, и он не имеет ничего общего с созданием нового шаблона фабрики. – opensas 18 August 2014 в 13:51
  • 4
    как насчет var obj = {} и obj.a = obj – neaumusic 5 May 2015 в 22:40
  • 5
    Я не понимаю эту функцию. Предположим, что from.constructor - Date. Как будет проведен третий тест if, когда 2-й тест if будет успешным и, заставить функцию вернуться (поскольку Date != Object && Date != Array)? – Adam McKee 1 September 2015 в 02:10
  • 6
    @AdamMcKee Поскольку передача аргументов javascript и назначение переменной сложны . Этот подход отлично работает, включая даты (которые действительно обрабатываются вторым тестом) - скрипка для тестирования здесь: jsfiddle.net/zqv9q9c6 . – brichins 14 June 2016 в 20:39
  • 7
    * Я имел в виду "назначение параметров" (внутри тела функции), а не «присвоение переменной». Хотя это помогает понять и тщательно. – brichins 15 June 2016 в 16:42
  • 8
    Хотя это работает для предопределенных объектов, «клонирование» таким образом, не будет распознавать новые свойства, добавленные к исходному объекту. Если вы создадите a, добавьте новое свойство в a, а затем создайте b. b не будет нового свойства. По существу фабричная модель неизменна для новых свойств. Это не парадигматически клонирует. См .: jsfiddle.net/jzumbrun/42xejnbx – Jon 3 September 2016 в 13:59
  • 9
    Я думаю, что это хороший совет, как правило, поскольку вместо использования const defaultFoo = { a: { b: 123 } }; вы можете пойти const defaultFoo = () => ({ a: { b: 123 } };, и ваша проблема решена. Однако на самом деле это не ответ на вопрос. Это могло бы иметь больше смысла в качестве комментария к вопросу, а не полного ответа. – Josh from Qaribou 6 February 2017 в 15:25
  • 10
    @NickSweeting: Попробуйте - возможно, это сработает. Если нет - исправьте и обновите ответ. Вот как это работает в сообществе :) – Kamarey 10 July 2017 в 07:20

Код:

// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
    if (from == null || typeof from != "object") return from;
    if (from.constructor != Object && from.constructor != Array) return from;
    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
        from.constructor == String || from.constructor == Number || from.constructor == Boolean)
        return new from.constructor(from);

    to = to || new from.constructor();

    for (var name in from)
    {
        to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
    }

    return to;
}

Тест:

var obj =
{
    date: new Date(),
    func: function(q) { return 1 + q; },
    num: 123,
    text: "asdasd",
    array: [1, "asd"],
    regex: new RegExp(/aaa/i),
    subobj:
    {
        num: 234,
        text: "asdsaD"
    }
}

var clone = extend(obj);
91
ответ дан 2 revs 15 August 2018 в 22:47
поделиться
  • 1
    как насчет var obj = {} и obj.a = obj – neaumusic 5 May 2015 в 22:40
  • 2
    Я не понимаю эту функцию. Предположим, что from.constructor - Date. Как будет проведен третий тест if, когда 2-й тест if будет успешным и, заставить функцию вернуться (поскольку Date != Object && Date != Array)? – Adam McKee 1 September 2015 в 02:10
  • 3
    @AdamMcKee Поскольку передача аргументов javascript и назначение переменной сложны . Этот подход отлично работает, включая даты (которые действительно обрабатываются вторым тестом) - скрипка для тестирования здесь: jsfiddle.net/zqv9q9c6 . – brichins 14 June 2016 в 20:39
  • 4
    * Я имел в виду "назначение параметров" (внутри тела функции), а не «присвоение переменной». Хотя это помогает понять и тщательно. – brichins 15 June 2016 в 16:42
  • 5
    @NickSweeting: Попробуйте - возможно, это сработает. Если нет - исправьте и обновите ответ. Вот как это работает в сообществе :) – Kamarey 10 July 2017 в 07:20
Другие вопросы по тегам:

Похожие вопросы: