Клонирование объекта в 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;
};
Ответ 2
Я удивлен, что Object.assign
не упоминался.
let cloned = Object.assign({}, source);
Если доступно (например, Babel), вы можете использовать оператор распространения объектов:
let cloned = { ... source };
Ответ 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));
Ответ 5
Вы можете использовать функцию расширения из JQuery:
var newClone= jQuery.extend({}, oldObject);
var deepClone = jQuery.extend(true, {}, oldObject);
Существует также плагин Node.js:
https://github.com/shimondoodkin/nodejs-clone-extend
Чтобы сделать это без JQuery или плагина, прочитайте здесь:
http://my.opera.com/GreyWyvern/blog/show.dml/1725165
Ответ 6
Отъезд underscore.js. Он имеет clone и extend и многие другие очень полезные функции.
Это может быть полезно: Использование модуля Underscore с Node.js
Ответ 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". Это хорошо работает для меня. Я не беру на себя ответственность за это. Опять же, я изменил только то, как оно определяется.
Ответ 13
Вы также можете использовать SugarJS в NodeJS.
http://sugarjs.com/
У них очень чистая функция клонирования:
http://sugarjs.com/api/Object/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
Ответ 18
В Node 11 версия является хорошим решением, скажем, пусть cloned = {... source};
[ https://stackoverflow.com/users/174445/djanowski]djanowski[1] это поможет мне.
Ответ 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
Надеюсь, что это будет полезно.