Как правильно клонировать объект JavaScript?

В качестве обходного пути я скомпилировал тест и выполнил тест из текущего каталога.

go test -c && ./<mypackage>.test

Или, если вы хотите использовать общую команду, которую вы можете использовать, вы можете переименовать тестовый файл с помощью опции -o.

go test -c -o xyz.test && ./xyz.test

2868
задан Shahnewaz 5 December 2018 в 00:09
поделиться

9 ответов

Чтобы сделать это для любого объекта в JavaScript не будет просто или просто. Вы столкнетесь с проблемой ошибочного взятия атрибутов от прототипа объекта, который нужно оставить в прототипе и не скопировать в новый экземпляр. Если, например, Вы добавляете a clone метод к Object.prototype, поскольку некоторые ответы изображают, необходимо будет явно пропустить тот атрибут. Но что, если существуют другие дополнительные методы, добавленные к Object.prototype, или другие промежуточные прототипы, о которых Вы не знаете? В этом случае Вы скопируете атрибуты, Вы не были должны, таким образом, необходимо обнаружить непредвиденные, нелокальные атрибуты с hasOwnProperty метод.

В дополнение к несчетным атрибутам Вы встретитесь с более жесткой проблемой, когда Вы попытаетесь скопировать объекты, которые скрыли свойства. Например, prototype скрытое свойство функции. Кроме того, на прототип объекта ссылаются с атрибутом __proto__, который также скрыт и не будет скопирован для/в итерации цикла по атрибутам исходного объекта. Я думаю __proto__ могло бы быть характерно для интерпретатора JavaScript Firefox, и это может быть что-то другое в других браузерах, но Вы получаете изображение. Не все является счетным. Можно скопировать скрытый атрибут, если Вы знаете его имя, но я не знаю ни о каком способе обнаружить его автоматически.

Еще одно препятствие в поисках изящного решения является проблемой установки опытного наследования правильно. Если прототип Вашего исходного объекта Object, затем просто создавая новый общий объект с {} будет работать, но если прототип источника является некоторым потомком Object, затем Вы собираетесь быть пропавшими без вести дополнительных участников от того прототипа, который Вы пропустили использование hasOwnProperty фильтр, или которые были в прототипе, но не были счетными во-первых. Одно решение могло бы состоять в том, чтобы назвать исходный объект constructor свойство, чтобы получить первоначальную копию возражает и затем скопировать по атрибутам, но затем Вы все еще не получите несчетные атрибуты. Например, a 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 также был бы один из 6 простых типов в том списке. Это может быть выполнено с кодом как следующее:

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, но это может быть достаточно во многих целях, пока Вы не предполагаете, что это будет просто работать на что-либо, что Вы бросаете в него.

1494
ответ дан 22 November 2019 в 19:47
поделиться

Объектное использование копии (... )

//bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2,c: 3 }

//good
const origional_obj = { id: 5, name: 'San Francisco'};
const copy_object = {...origional_obj, pincode: 4444};
console.log(copy_object)  //{ id: 5, name: 'San Francisco', pincode: 4444 }

То же может быть использованием для массив копирования от одного до другого

const itemsCopy = [...items];
0
ответ дан GANESH CHOKHARE 14 September 2019 в 15:55
поделиться

От этой статьи: Как скопировать массивы и объекты в JavaScript Brian Huisman:

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

Вот функция, которую можно использовать.

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

Одно особенно неэлегантное решение - использовать кодировку 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
40
ответ дан 22 November 2019 в 19:47
поделиться

Использование lodash _ .cloneDeep ().

Мелкая Копия: lodash _ .clone ()

А мелкая копия может быть сделан путем простого копирования ссылки.

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.clone(obj1);
obj1.a = 4;
obj1.b.c = 4;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
//{"a":4,"b":{"c":4,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
//{"a":0,"b":{"c":4,"e":{"f":100}}}

Shallow Copy: lodash _.clone()

Глубокая Копия: lodash _ .cloneDeep ()

поля разыменовываются: вместо ссылок на объекты, скопированные

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.cloneDeep(obj1);
obj1.a = 100;
obj1.b.c = 100;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
{"a":100,"b":{"c":100,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
{"a":0,"b":{"c":0,"e":{"f":0}}}

Deep Copy: lodash _.cloneDeep()

5
ответ дан 22 November 2019 в 19:47
поделиться

Если нет никаких круговых зависимостей в Вашем объекте, я предлагаю использовать один из других ответов или методов копии jQuery, поскольку они все кажутся довольно эффективными.

Если существуют круговые зависимости (т.е. два подобъекта связываются друг с другом), Вы отчасти завинчены, поскольку нет (с теоретической точки зрения) никакого способа решить эту проблему изящно.

1
ответ дан 22 November 2019 в 19:47
поделиться

Простой

var restore = { name:'charlesi',
age:9}
var prev_data ={
name: 'charles'
age : 10
}

var temp = JSON.stringify(prev_data)
restore = JSON.parse(temp)

restore = {
name:'charlie',
age : 12}

производит prev_data:

{
name: 'charles'
age : 10
} 
2
ответ дан 22 November 2019 в 19:47
поделиться

Из инструкций по кодированию JavaScript Apple:

// Create an inner object with a variable x whose default
// value is 3.
function innerObj()
{
        this.x = 3;
}
innerObj.prototype.clone = function() {
    var temp = new innerObj();
    for (myvar in this) {
        // this object does not contain any objects, so
        // use the lightweight copy code.
        temp[myvar] = this[myvar];
    }
    return temp;
}

// Create an outer object with a variable y whose default
// value is 77.
function outerObj()
{
        // The outer object contains an inner object.  Allocate it here.
        this.inner = new innerObj();
        this.y = 77;
}
outerObj.prototype.clone = function() {
    var temp = new outerObj();
    for (myvar in this) {
        if (this[myvar].clone) {
            // This variable contains an object with a
            // clone operator.  Call it to create a copy.
            temp[myvar] = this[myvar].clone();
        } else {
            // This variable contains a scalar value,
            // a string value, or an object with no
            // clone function.  Assign it directly.
            temp[myvar] = this[myvar];
        }
    }
    return temp;
}

// Allocate an outer object and assign non-default values to variables in
// both the outer and inner objects.
outer = new outerObj;
outer.inner.x = 4;
outer.y = 16;

// Clone the outer object (which, in turn, clones the inner object).
newouter = outer.clone();

// Verify that both values were copied.
alert('inner x is '+newouter.inner.x); // prints 4
alert('y is '+newouter.y); // prints 16

Steve

0
ответ дан 22 November 2019 в 19:47
поделиться
Другие вопросы по тегам:

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