Создайте постоянную/неизменяемую копию любого объекта (включая глубокие свойства)
Как я могу создать неизменяемую/неизменяемую версию объекта в JavaScript, свойства которого не могут быть изменены? Это также должно относиться к свойствам любых под-объектов и т.д.
Все методы, с которыми мне приходилось сталкиваться (Object.defineProperty
, Object.freeze
и т.д.) работают только для свойств верхнего уровня объекта, но не для под-объектов.
(Возможный прецедент: после создания/модификации объекта типа settings
или configuration
в определенном модуле, вам нужно подвергнуть его остальным программным модулям в неизменяемой форме.)
Ответы
Ответ 1
Это решение, с которым я придумал, подумав. Хорошо работает для моих нужд, поэтому я решил поделиться им с QNA.
Предлагайте любые улучшения/проблемы, если вы их найдете.
/**
* Make the the specified object (deeply) immutable or "read-only", so that none of its
* properties (or sub-properties) can be modified. The converted object is returned.
* @param {object} obj Input object
*/
makeImmutable: function makeImmutable (obj) {
if ((typeof obj === "object" && obj !== null) ||
(Array.isArray? Array.isArray(obj): obj instanceof Array) ||
(typeof obj === "function")) {
Object.freeze(obj);
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
makeImmutable(obj[key]);
}
}
}
return obj;
}
EDIT:
Упрощен код. Также корректно обрабатывает массивы.
Ответ 2
Заинтриговано этим решением.
Вот фрагмент кода с примером:
/**
* Make the the specified object (deeply) immutable or "read-only", so that none of its
* properties (or sub-properties) can be modified. The converted object is returned.
* @param {object} obj Input object
*/
makeImmutable: function makeImmutable(obj) {
if ((typeof obj === "object" && obj !== null) ||
(Array.isArray ? Array.isArray(obj) : obj instanceof Array) ||
(typeof obj === "function")) {
Object.freeze(obj);
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
makeImmutable(obj[key]);
}
}
}
return obj;
}
var newObj = {
thisArrayOfObjects: [{
propertyOne: 'value1',
propertyTwo: 'value2'
}]
};
newObj.thisArrayOfObjects.push({
propertyBefore: 'before3'
});
console.log('newObj', newObj);
$('#viewer').append('newObj: ' + JSON.stringify(newObj));
makeImmutable(newObj);
console.log('imutable', newObj);
$('#viewer').append('<br/><br/>imutable: ' + JSON.stringify(newObj));
try {
newObj.thisArrayOfObjects.push({
propertyThree: 'value3'
});
} catch (e) {
$('#viewer').append('<br/><br/>immutable error: ' + e.message);
console.log('immutable error:', e.message);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="viewer" />
Ответ 3
Отличные ответы. Этот использует lodash
:
var _ = require('lodash');
Чтобы сделать объект неизменным:
/**
* Makes an Object immutable by (deep) freezing all own peoperties.
* @param {*} obj - Object to make immutable.
* @returns {*} The input Object.
*/
function deepFreeze(obj) {
if (_.isObject(obj) || _.isArray(obj) || _.isFunction(obj)) {
Object.freeze(obj);
_.forOwn(obj, deepFreeze);
}
return obj;
}
Чтобы сделать неизменяемый клон:
var frozenClone = deepFreeze(_.cloneDeep(obj));