Как правильно клонировать объект JavaScript? - PullRequest
2787 голосов
/ 08 апреля 2009

У меня есть объект, x. Я хотел бы скопировать его как объект y, чтобы изменения в y не изменяли x. Я понял, что копирование объектов, полученных из встроенных объектов JavaScript, приведет к появлению дополнительных нежелательных свойств. Это не проблема, так как я копирую один из своих объектов, созданных в буквальном смысле.

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

Ответы [ 63 ]

0 голосов
/ 06 мая 2013

Мои любимые и элегантные объекты JS клон решение -

function CloneObject() {}
function cloneObject(o) {
   CloneObject.prototype = o;
   return new CloneObject();
}

Используйте cloneObject(object), чтобы получить клон объекта JS.

В отличие от многих копий решений, этот клон сохраняет отношения прототипа в клонированном объекте.

0 голосов
/ 27 апреля 2018

Я даю ответ на этот вопрос, потому что я не вижу здесь никаких собственных, рекурсивных реализаций, которые бы решали проблему DOM элементов.

Проблема в том, что <element> имеет атрибуты parent и child, которые ссылаются на другие элементы со значениями parent и child, которые указывают на исходные <element>, вызывая либо бесконечная рекурсивная или циклическая избыточность .

Если ваш объект является чем-то безопасным и простым, например

{
    '123':456
}

... тогда любой другой ответ здесь, вероятно, сработает.

Но если у вас есть ...

{
    '123':<reactJSComponent>,
    '456':document.createElement('div'),
}

... тогда вам нужно что-то вроде этого:

    // cloneVariable() : Clone variable, return null for elements or components.
var cloneVariable = function (args) {
    const variable = args.variable;

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

    if(typeof(variable) === 'object') {
            if(variable instanceof HTMLElement || variable.nodeType > 0) {
                    return null;
            }

            if(Array.isArray(variable)) {
                    var arrayclone = [];

                    variable.forEach((element) => {
                            arrayclone.push(cloneVariable({'variable':element}));
                    });

                    return arrayclone;
            }

            var objectclone = {};

            Object.keys(variable).forEach((field) => {
                    objectclone[field] = cloneVariable({'variable':variable[field]});
            });

            return objectclone;
    }

    return variable;
}
0 голосов
/ 27 марта 2018

Вы можете клонировать свой объект без модификации родительского объекта -

    /** [Object Extend]*/
    ( typeof Object.extend === 'function' ? undefined : ( Object.extend = function ( destination, source ) {
        for ( var property in source )
            destination[property] = source[property];
        return destination;
    } ) );
    /** [/Object Extend]*/
    /** [Object clone]*/
    ( typeof Object.clone === 'function' ? undefined : ( Object.clone = function ( object ) {
        return this.extend( {}, object );
    } ) );
    /** [/Object clone]*/

    let myObj = {
        a:1, b:2, c:3, d:{
            a:1, b:2, c:3
        }
    };

    let clone = Object.clone( myObj );

    clone.a = 10;

    console.log('clone.a==>', clone.a); //==> 10

    console.log('myObj.a==>', myObj.a); //==> 1 // object not modified here

    let clone2 = Object.clone( clone );

    clone2.a = 20;

    console.log('clone2.a==>', clone2.a); //==> 20

    console.log('clone.a==>', clone.a); //==> 10 // object not modified here
0 голосов
/ 09 февраля 2014

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

Я использую функцию toType () для явного возврата типа объекта. У меня также есть собственная функция copyObj () , которая довольно похожа по логике и отвечает на все три случая Object (), Array () и Date ().

Я запускаю его в NodeJS.

НЕ ИСПЫТАНО, ДА.

// Returns true, if one of the parent's children is the target.
// This is useful, for avoiding copyObj() through an infinite loop!
function isChild(target, parent) {
  if (toType(parent) == '[object Object]') {
    for (var name in parent) {
      var curProperty = parent[name];

      // Direct child.
      if (curProperty = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curProperty) == '[object Object]' || toType(curProperty) == '[object Array]') {
        if (isChild(target, curProperty)) return true;
      }
    }
  } else if (toType(parent) == '[object Array]') {
    for (var i=0; i < parent.length; i++) {
      var curItem = parent[i];

      // Direct child.
      if (curItem = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curItem) == '[object Object]' || toType(curItem) == '[object Array]') {
        if (isChild(target, curItem)) return true;
      }
    }
  }

  return false;     // Not the target.
}
0 голосов
/ 17 октября 2017

Я не знаю, в каких случаях это не работает, но у меня есть копия массива. Я думаю, что это мило :) Надеюсь, что это помогает

copiedArr = origArr.filter(function(x){return true})
0 голосов
/ 27 июля 2013
//
// creates 'clone' method on context object
//
//  var 
//     clon = Object.clone( anyValue );
//
!((function (propertyName, definition) {
    this[propertyName] = definition();
}).call(
    Object,
    "clone",
    function () {
        function isfn(fn) {
            return typeof fn === "function";
        }

        function isobj(o) {
            return o === Object(o);
        }

        function isarray(o) {
            return Object.prototype.toString.call(o) === "[object Array]";
        }

        function fnclon(fn) {
            return function () {
                fn.apply(this, arguments);
            };
        }

        function owns(obj, p) {
            return obj.hasOwnProperty(p);
        }

        function isemptyobj(obj) {
            for (var p in obj) {
                return false;
            }
            return true;
        }

        function isObject(o) {
            return Object.prototype.toString.call(o) === "[object Object]";
        }
        return function (input) {
            if (isfn(input)) {
                return fnclon(input);
            } else if (isobj(input)) {
                var cloned = {};
                for (var p in input) {
                    owns(Object.prototype, p)
                    || (
                        isfn(input[p])
                        && ( cloned[p] = function () { return input[p].apply(input, arguments); } )
                        || ( cloned[p] = input[p] )
                    );
                }
                if (isarray(input)) {
                    cloned.length = input.length;
                    "concat every filter forEach indexOf join lastIndexOf map pop push reduce reduceRight reverse shift slice some sort splice toLocaleString toString unshift"
                    .split(" ")
                    .forEach(
                      function (methodName) {
                        isfn( Array.prototype[methodName] )
                        && (
                            cloned[methodName] =
                            function () {
                                return Array.prototype[methodName].apply(cloned, arguments);
                            }
                        );
                      }
                    );
                }
                return isemptyobj(cloned)
                       ? (
                          isObject(input)
                          ? cloned
                          : input
                        )
                       : cloned;
            } else {
                return input;
            }
        };
    }
));
//
0 голосов
/ 19 мая 2017

Хорошо, я знаю, что у него много ответов, но никто не указал, в EcmaScript5 есть метод assign, работа с FF и Chrome, он копирует перечисляемые и собственные свойства и символы.

Назначение объекта

0 голосов
/ 31 августа 2013
function clone(obj)
{
    var cloneObj = Object.create(obj);

    return cloneObj;
}

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

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

0 голосов
/ 07 сентября 2013

Я пришел на эту страницу из-за того же вопроса, но я не использую JQuery, и ни один из методов-клонов не работал для моих собственных объектов.

Я знаю, что мой ответ не слишком сильно связан с этим вопросом, потому что это другой подход. Вместо использования функций-клонов я использую функцию создания. Это сработало для меня в следующих (к сожалению ограничивающих) целях:

  1. Я использую в основном Javascript, сгенерированный JSP
  2. В начале я знаю, какой объект должен быть сгенерирован (в моем случае это информация из базы данных, которая выбирается один раз и должна чаще развертываться в JS.

Сначала я определил свои объекты следующим образом:

var obj= new Object();
obj.Type='Row';
obj.ID=1;
obj.Value='Blah blah';

Теперь я переместил все как:

function getObjSelektor(id_nummer,selected){
var obj = document.createElement("select");
obj.setAttribute("id","Selektor_"+id_nummer);
obj.setAttribute("name","Selektor");
obj.setAttribute("size","1");

var obj_opt_1 = document.createElement("option");
obj_opt_1.setAttribute("value","1");
if(1==selected)
    posopval_opt_1.setAttribute("selected","selected");
obj_opt_1.innerHTML="Blah blah";
obj.appendChild(obj_opt_1);

var obj_opt_2 = document.createElement("option");
obj_opt_2.setAttribute("value","2");
if(2==selected)
    obj_opt_2.setAttribute("selected","selected");
obj_opt_2.innerHTML="2nd Row";
obj.appendChild(obj_opt_2);

...

return obj;
}

И вызовите функцию в обычном коде:

myDiv.getObjSelektor(getObjSelektor(anotherObject.ID));

Как уже говорилось, это другой подход, который решил мою проблему для моих целей.

0 голосов
/ 10 ноября 2017

Если ваш объект является классом (например, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes):

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

Тогда в copiedObject у вас есть глубоко скопированный экземпляр класса originalObject со всеми его методами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...