Если я клонирую массив, я использую cloneArr = arr.slice()
Я хочу знать, как клонировать объект в nodejs.
Если я клонирую массив, я использую cloneArr = arr.slice()
Я хочу знать, как клонировать объект в nodejs.
Для утилит и классов, где нет необходимости сжимать каждую каплю производительности, я часто обманываю и просто использую JSON для выполнения глубокой копии:
function clone(a) {
return JSON.parse(JSON.stringify(a));
}
Это не единственный ответ или самый изящный ответ; все остальные ответы должны рассматриваться для узких мест производства. Однако это быстрое и грязное решение, достаточно эффективное и полезное в большинстве ситуаций, где я бы клонировал простой хэш свойств.
Есть несколько модулей Node, если вы не хотите "сворачивать свои собственные". Этот выглядит хорошо: https://www.npmjs.com/package/clone
Похоже, что он обрабатывает всевозможные вещи, включая круговые ссылки. На странице github:
Клонирование мастеров клонирования объектов, массивов, объектов Date и RegEx объекты. Все клонируется рекурсивно, так что вы можете клонировать даты например, в массивах объектов. [...] Циркулярные ссылки? Да!
Object.assign не упоминается ни в одном из приведенных выше ответов.
let cloned = Object.assign({}, source);
Если вы используете ES6, вы можете использовать оператор спреда:
let cloned = { ... source };
Ссылка: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Не существует встроенного метода клонирования объектов. 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
для мелких клонов
Трудно выполнить общую, но полезную операцию клонирования, потому что то, что должно быть клонировано рекурсивно, и что нужно просто скопировать, зависит от того, как должен работать конкретный объект.
Что-то, что может быть полезно,
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)
.
Для мелкой копии мне нравится использовать шаблон сокращения (обычно в модуле или такой), например:
var newObject = Object.keys(original).reduce(function (obj, item) {
obj[item] = original[item];
return obj;
},{});
Здесь jsperf для пары опций: http://jsperf.com/shallow-copying
Старый вопрос, но есть более элегантный ответ, чем то, что было предложено до сих пор; используйте встроенный 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.
Надеюсь, это поможет кому-то другому; ссылки на объекты ссылаются на страдания, и это решает каждый раз!
Вы можете использовать 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]);
В зависимости от того, что вы хотите сделать с клонированным объектом, вы можете использовать механизм прототипного наследования javascript и достичь клонированного объекта через:
var clonedObject = Object.create(originalObject);
Просто помните, что это не полный клон - лучше или хуже.
Хорошо, что вы на самом деле не дублировали объект, поэтому объем памяти будет низким.
Некоторые сложные вещи, которые следует помнить об этом методе, это то, что итерация свойств, определенных в цепочке прототипов, иногда немного отличается и тот факт, что любые изменения исходного объекта будут влиять и на клонированный объект, если это свойство не было установлено и сам по себе.
Я реализовал полную глубокую копию. Я считаю, что это лучший выбор для универсального метода клонирования, но он не обрабатывает циклические ссылки.
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;
}
С помощью этой копии я не могу найти никакой разницы между оригиналом и скопированным, за исключением того, что оригинал использовал закрытие при его построении, поэтому я считаю его хорошей реализацией.
Я надеюсь, что это поможет
для массива, можно использовать
var arr = [1,2,3];
var arr_2 = arr ;
print ( arr_2 );
обр = arr.slice(0);
print ( arr );
arr[1]=9999;
print ( arr_2 );