Nodejs: как клонировать объект

Если я клонирую массив, я использую cloneArr = arr.slice()

Я хочу знать, как клонировать объект в nodejs.

Ответ 1

Для утилит и классов, где нет необходимости сжимать каждую каплю производительности, я часто обманываю и просто использую JSON для выполнения глубокой копии:

function clone(a) {
   return JSON.parse(JSON.stringify(a));
}

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

Ответ 2

Есть несколько модулей Node, если вы не хотите "сворачивать свои собственные". Этот выглядит хорошо: https://www.npmjs.com/package/clone

Похоже, что он обрабатывает всевозможные вещи, включая круговые ссылки. На странице github:

Клонирование мастеров клонирования объектов, массивов, объектов Date и RegEx объекты. Все клонируется рекурсивно, так что вы можете клонировать даты например, в массивах объектов. [...] Циркулярные ссылки? Да!

Ответ 4

Не существует встроенного метода клонирования объектов. Underscore реализует _.clone, который является мелким клоном.

_.clone = function(obj) {
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

Он либо разрезает его, либо расширяет его.

Здесь _.extend

// extend the obj (first parameter)
_.extend = function(obj) {
  // for each other parameter
  each(slice.call(arguments, 1), function(source) {
    // loop through all properties of the other objects
    for (var prop in source) {
      // if the property is not undefined then add it to the object.
      if (source[prop] !== void 0) obj[prop] = source[prop];
    }
  });
  // return the object (first parameter)
  return obj;
};

Расширять просто итерации через все элементы и создает новый объект с элементами в нем.

Вы можете развернуть свою наивную реализацию, если хотите

function clone(o) {
  var ret = {};
  Object.keys(o).forEach(function (val) {
    ret[val] = o[val];
  });
  return ret;
}

Есть хорошие причины, чтобы избежать глубокого клонирования, потому что закрытие не может быть клонировано.

Я лично задал вопрос о deep cloning objects before, и я пришел к выводу, что вы просто этого не делаете.

Моя рекомендация - использование underscore и _.clone для мелких клонов

Ответ 5

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

Что-то, что может быть полезно,

function clone(x)
{
    if (x === null || x === undefined)
        return x;
    if (x.clone)
        return x.clone();
    if (x.constructor == Array)
    {
        var r = [];
        for (var i=0,n=x.length; i<n; i++)
            r.push(clone(x[i]));
        return r;
    }
    return x;
}

В этом коде логика

  • в случае null или undefined просто вернуть то же самое (требуется специальный случай, потому что это ошибка, чтобы попытаться увидеть, существует ли метод clone)
  • У объекта есть метод clone? затем используйте
  • является объектом массива? затем выполните операцию рекурсивного клонирования
  • иначе вернуть одно и то же значение

Эта функция клонирования должна позволить легко реализовать пользовательские методы клонирования... например

function Point(x, y)
{
    this.x = x;
    this.y = y;

    ...
}

Point.prototype.clone = function()
{
    return new Point(this.x, this.y);
};



function Polygon(points, style)
{
    this.points = points;
    this.style = style;

    ...
}

Polygon.prototype.clone = function()
{
    return new Polygon(clone(this.points),
                       this.style);
};

Когда в объекте вы знаете, что правильная операция клонирования для определенного массива является только мелкой копией, вы можете вызвать values.slice() вместо clone(values).

Например, в приведенном выше коде я явно требую, чтобы клонирование объекта polygon клонировало точки, но будет иметь один и тот же объект стиля. Если я захочу также клонировать объект стиля, я могу просто передать clone(this.style).

Ответ 6

Для мелкой копии мне нравится использовать шаблон сокращения (обычно в модуле или такой), например:

var newObject = Object.keys(original).reduce(function (obj, item) {
    obj[item] = original[item];
    return obj;
},{});

Здесь jsperf для пары опций: http://jsperf.com/shallow-copying

Ответ 7

Старый вопрос, но есть более элегантный ответ, чем то, что было предложено до сих пор; используйте встроенный utils._extend:

var extend = require("util")._extend;

var varToCopy = { test: 12345, nested: { val: 6789 } };

var copiedObject = extend({}, varToCopy);

console.log(copiedObject);

// outputs:
// { test: 12345, nested: { val: 6789 } }

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

Используя приведенные выше примерные переменные, вы также можете сделать это:

var anotherMergeVar = { foo: "bar" };

extend(copiedObject, { anotherParam: 'value' }, anotherMergeVar);

console.log(copiedObject);

// outputs:
// { test: 12345, nested: { val: 6789 }, anotherParam: 'value', foo: 'bar' }

Очень удобная утилита, особенно, когда я использую расширение в AngularJS и jQuery.

Надеюсь, это поможет кому-то другому; ссылки на объекты ссылаются на страдания, и это решает каждый раз!

Ответ 8

Вы можете использовать lodash. Он имеет clone и cloneDeep методы.

var _= require('lodash');

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

var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);
// => true

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);

Ответ 9

В зависимости от того, что вы хотите сделать с клонированным объектом, вы можете использовать механизм прототипного наследования javascript и достичь клонированного объекта через:

var clonedObject = Object.create(originalObject);

Просто помните, что это не полный клон - лучше или хуже.

Хорошо, что вы на самом деле не дублировали объект, поэтому объем памяти будет низким.

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

Ответ 10

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

Пример использования:

parent = {'prop_chain':3}
obj = Object.create(parent)
obj.a=0; obj.b=1; obj.c=2;

obj2 = copy(obj)

console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3
console.log(obj2, obj2.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3

parent.prop_chain=4
obj2.a = 15

console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 4
console.log(obj2, obj2.prop_chain)
// '{'a':15, 'b':1, 'c':2} 4

Сам код:

Этот код копирует объекты со своими прототипами, он также копирует функции (может быть полезен для кого-то).

function copy(obj) {
  // (F.prototype will hold the object prototype chain)
  function F() {}
  var newObj;

  if(typeof obj.clone === 'function')
    return obj.clone()

  // To copy something that is not an object, just return it:
  if(typeof obj !== 'object' && typeof obj !== 'function' || obj == null)
    return obj;

  if(typeof obj === 'object') {    
    // Copy the prototype:
    newObj = {}
    var proto = Object.getPrototypeOf(obj)
    Object.setPrototypeOf(newObj, proto)
  } else {
    // If the object is a function the function evaluate it:
    var aux
    newObj = eval('aux='+obj.toString())
    // And copy the prototype:
    newObj.prototype = obj.prototype
  }

  // Copy the object normal properties with a deep copy:
  for(var i in obj) {
    if(obj.hasOwnProperty(i)) {
      if(typeof obj[i] !== 'object')
        newObj[i] = obj[i]
      else
        newObj[i] = copy(obj[i])
    }
  }

  return newObj;
}

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

Я надеюсь, что это поможет

Ответ 11

для массива, можно использовать

var arr = [1,2,3]; 
var arr_2 = arr ; 

print ( arr_2 ); 

обр = arr.slice(0);

print ( arr ); 

arr[1]=9999; 

print ( arr_2 );