Каков наиболее эффективный способ глубокого клонирования объекта в JavaScript?

Я столкнулся с этой проблемой много лет назад.

Проблема в том, что, когда вы используете свойство innerHTML для добавления HTML, после каждого обновления базовый движок закрывает закрытый тег (и исправляет другой плохой HTML) для вас. Поэтому после второй строки теги

и автоматически закрываются, и все содержимое после этого будет просто записано вне таблицы.


Метод 1 (простой способ)

Используйте строку для хранения HTML для всей таблицы и обновите ее сразу.

var HTML = "
"; for(j=1;j<=10;j++) { HTML += ""; } HTML += "
"+String.fromCharCode(j+64)+"
"; document.getElementById("outputDiv").innerHTML = HTML;

Fiddle


Способ 2 (лучший способ)

Использовать функции DOM

var table = document.createElement('table');
table.setAttribute('border','1');
table.setAttribute('width','100%')
var row = table.insertRow(0);
for(j=1; j<=10; j++){
    var text = document.createTextNode(String.fromCharCode(j+64));
    var cell = row.insertCell(j-1);
    cell.setAttribute('align','center')
    cell.appendChild(text);
}
document.getElementById("outputDiv").appendChild(table);

Fiddle


Способ 2 улучшен (Еще лучше)

Вместо атрибутов HTML используйте CSS . Последний обычно обесценивается по последним спецификациям.

Отличным ресурсом для начала обучения CSS является Mozilla Developer Network

Fiddle


Способ 3 (длинный путь, но лучший в долгосрочной перспективе)

Используйте jQuery .

$('').append('').appendTo('#outputDiv');
for(j=1; j<=10; j++)
    $('
').text(String.fromCharCode(j+64)).appendTo('tr');

Fiddle

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

8 ответов

2019 - примечание в июне: Это было первоначально ответом на другой ответ, не надлежащим ответом на этот вопрос. Никакая идея, почему это было выбрано как правильный ответ. Но так как upvotes разросся, и это - безусловно ответ № 1 на этот вопрос, это будет суммировать решения как ответ Wiki.

Собственный компонент глубоко клонирование

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

Быстрое клонирование с потерей данных - JSON.parse/stringify

, Если Вы не используете Date с, функции, undefined, Infinity, RegExps, Карты, Наборы, Блобы, FileLists, ImageDatas, разреженные массивы, Типизированные массивы или другие составные типы в Вашем объекте, очень простой один лайнер для глубокого клонирования объекта:

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'
  re: /.*/,  // lost
}
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()

См. ответ Corban для сравнительных тестов.

Надежное клонирование, пользующееся библиотекой

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

  • lodash - cloneDeep ; может быть импортирован отдельно через модуль lodash.clonedeep и, вероятно, Ваш лучший выбор, если Вы уже не пользуетесь библиотекой, которая обеспечивает глубокую функцию клонирования
  • AngularJS - angular.copy
  • jQuery - jQuery.extend(true, { }, oldObject) ; .clone() только клоны элементы DOM

ES6

Для полноты, обратите внимание, что ES6 предлагает два мелких механизма копии: Object.assign() и оператор .

распространения
4379
ответ дан 14 revs, 12 users 26% 22 March 2017 в 16:17
поделиться
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }
21
ответ дан Mark Cidade 22 March 2017 в 16:17
поделиться
  • 1
    Извините, я добавил комментарий, но Вы ответили на него правильно, +1. – Hans Passant 23 June 2012 в 00:46

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

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;
}
310
ответ дан 11 revs, 9 users 30% 22 March 2017 в 16:17
поделиться
  • 1
    @drax Извините я don' t получают его. Вы говорите, что какой-либо шаблон функции является проблемой? What' s с, о, половина стандартной библиотеки, он бесполезный теперь потому что " Вы или авторизовываете несколько версий своей функции с типами, которые не были бы необходимый иметь смысл, или Вы явно запрещаете их, которые добавляют некоторый ужасный code"? комитет по стандартам будет удивлен услышать это. – n.m. 28 January 2014 в 17:45

Код:

// 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);
95
ответ дан 22 November 2019 в 19:37
поделиться

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

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;
}
93
ответ дан 22 November 2019 в 19:37
поделиться
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});
63
ответ дан 22 November 2019 в 19:37
поделиться
// 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;
};
11
ответ дан 22 November 2019 в 19:37
поделиться

Это - мое решение, не пользуясь никакой библиотекой или собственной функцией JavaScript.

function deepClone(obj) {
  if (typeof obj !== "object") {
    return obj;
  } else {
    let newObj =
      typeof obj === "object" && obj.length !== undefined ? [] : {};
    for (let key in obj) {
      if (key) {
        newObj[key] = deepClone(obj[key]);
      }
    }
    return newObj;
  }
}
1
ответ дан 22 November 2019 в 19:37
поделиться
Другие вопросы по тегам:

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