Клонирование объекта в Node.js

Каков наилучший способ клонирования объекта в node.js

например. Я хочу избежать ситуации, когда:

var obj1 = {x: 5, y:5};
var obj2 = obj1;
obj2.x = 6;
console.log(obj1.x); // logs 6

Объект может содержать сложные типы как атрибуты, поэтому простой для (var x in obj1) не будет разрешать. Нужно ли мне писать рекурсивный клон или есть что-то построенное, что я не вижу?

Ответ 1

Возможность 1

Глубокая копия с низким вырезом:

var obj2 = JSON.parse(JSON.stringify(obj1));

Возможность 2 (устаревшая)

Внимание: Это решение теперь отмечено как устаревшее в документации Node.js:

Метод util._extend() никогда не предназначался для использования вне внутренних модулей Node.js. Сообщество нашло и использовало его в любом случае.

Он устарел и не должен использоваться в новом коде. JavaScript поставляется с очень похожими встроенными функциями через Object.assign().

Оригинальный ответ::

Для мелкой копии используйте Node встроенную функцию util._extend().

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

var obj1 = {x: 5, y:5};
var obj2 = extend({}, obj1);
obj2.x = 6;
console.log(obj1.x); // still logs 5

Исходный код функции Node _extend находится здесь: https://github.com/joyent/node/blob/master/lib/util.js

exports._extend = function(origin, add) {
  // Don't do anything if add isn't an object
  if (!add || typeof add !== 'object') return origin;

  var keys = Object.keys(add);
  var i = keys.length;
  while (i--) {
    origin[keys[i]] = add[keys[i]];
  }
  return origin;
};

Ответ 3

Object.defineProperty(Object.prototype, "extend", {
    enumerable: false,
    value: function(from) {
        var props = Object.getOwnPropertyNames(from);
        var dest = this;
        props.forEach(function(name) {
            if (name in dest) {
                var destination = Object.getOwnPropertyDescriptor(from, name);
                Object.defineProperty(dest, name, destination);
            }
        });
        return this;
    }
});

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

Ответ 4

var obj2 = JSON.parse(JSON.stringify(obj1));

Ответ 7

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

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

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

Ответ 8

Простой и самый быстрый способ клонирования объекта в NodeJS - использовать метод Object.keys(obj)

var a = {"a": "a11", "b": "avc"};
var b;

for(var keys = Object.keys(a), l = keys.length; l; --l)
{
   b[ keys[l-1] ] = a[ keys[l-1] ];
}
b.a = 0;

console.log("a: " + JSON.stringify(a)); // LOG: a: {"a":"a11","b":"avc"} 
console.log("b: " + JSON.stringify(b)); // LOG: b: {"a":0,"b":"avc"}

Для метода Object.keys требуется JavaScript 1.8.5; nodeJS v0.4.11 поддерживает этот метод

но, конечно, для вложенных объектов необходимо реализовать рекурсивную функцию


Другое решение - использовать встроенный JSON (реализован в JavaScript 1.7), но он намного медленнее (~ 10 раз медленнее), чем предыдущий

var a = {"a": i, "b": i*i};
var b = JSON.parse(JSON.stringify(a));
b.a = 0;

Ответ 9

Этот код также работает. Метод Object.create() создает новый объект с указанным объектом и свойствами прототипа.

 
var obj1 = {x:5, y:5};

var obj2 = Object.create(obj1);

obj2.x; //5
obj2.x = 6;
obj2.x; //6

obj1.x; //5

Ответ 10

Существует также проект Github, который должен быть более прямым портом jQuery.extend():

https://github.com/dreamerslab/node.extend

Пример, измененный из jQuery docs:

var extend = require('node.extend');

var object1 = {
    apple: 0,
    banana: {
        weight: 52,
        price: 100
    },
    cherry: 97
};

var object2 = {
    banana: {
        price: 200
    },
    durian: 100
};

var merged = extend(object1, object2);

Ответ 11

Существует еще одна библиотека lodash, она имеет clone и cloneDeep, также много других полезных функций.

Ответ 12

В поисках истинного варианта клона я наткнулся на ссылку ridcully:

http://my.opera.com/GreyWyvern/blog/show.dml/1725165

Я изменил решение на этой странице, чтобы функция, прикрепленная к прототипу Object, не была перечислима. Вот мой результат:

Object.defineProperty(Object.prototype, 'clone', {
    enumerable: false,
    value: function() {
        var newObj = (this instanceof Array) ? [] : {};
        for (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;
    }
});

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

Ответ 14

Если вы используете кофе script, это просто:

newObject = {}
newObject[key] = value  for own key,value of oldObject

Хотя это не глубокий клон.

Ответ 15

Ни один из ответов не удовлетворил меня, некоторые не работают или просто мелкие клоны, ответы от @clint-harris и использование JSON.parse/stringify хороши, но довольно медленны. Я нашел модуль, который быстро клонирует: https://github.com/AlexeyKupershtokh/node-v8-clone

Ответ 16

Нет встроенного способа сделать настоящий клон (глубокую копию) объекта в node.js. Есть несколько сложных случаев, поэтому вы обязательно должны использовать библиотеку для этого. Я написал такую ​​функцию для моей simpleoo библиотеки. Вы можете использовать функцию deepCopy, не используя ничего из библиотеки (которая довольно мала), если она вам не нужна. Эта функция поддерживает клонирование нескольких типов данных, включая массивы, даты и регулярные выражения, поддерживает рекурсивные ссылки и также работает с объектами, функции конструкторов которых имеют требуемые параметры.

Вот код:

//If Object.create isn't already defined, we just do the simple shim, without the second argument,
//since that 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();
    };
}

/**
 * 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 = function deepCopy(src, /* INTERNAL */ _visited) {
    if(src == null || typeof(src) !== 'object'){
        return src;
    }

    // Initialize the visited objects array if needed
    // This is used to detect cyclic references
    if (_visited == undefined){
        _visited = [];
    }
    // Ensure src has not already been visited
    else {
        var i, len = _visited.length;
        for (i = 0; i < len; i++) {
            // If src was already visited, don't try to copy it, just return the reference
            if (src === _visited[i]) {
                return src;
            }
        }
    }

    // Add this object to the visited array
    _visited.push(src);

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

    //Special cases:
    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice(0) would soft clone
        ret = src.slice();
        var i = ret.length;
        while (i--){
            ret[i] = deepCopy(ret[i], _visited);
        }
        return ret;
    }
    //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);
    }

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

    //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 ret = object_create(proto);

    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
        ret[key] = deepCopy(src[key], _visited);
    }
    return ret;
};

Ответ 17

npm install node-v8-clone

Самый быстрый клонер, он открывает собственный метод клонирования из node.js

var clone = require('node-v8-clone').clone;
var newObj = clone(obj, true); //true - deep recursive clone

Ответ 19

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

function object () {
  this.x = 5;
  this.y = 5;
}
var obj1 = new object();
var obj2 = new object();
obj2.x = 6;
console.log(obj1.x); //logs 5

Вы также можете передать аргументы конструктору объекта

function object (x, y) {
   this.x = x;
   this.y = y;
}
var obj1 = new object(5, 5);
var obj2 = new object(6, 6);
console.log(obj1.x); //logs 5
console.log(obj2.x); //logs 6

Надеюсь, что это будет полезно.