创建任何对象的只读/不可变副本(包括深层属性)

Posted

技术标签:

【中文标题】创建任何对象的只读/不可变副本(包括深层属性)【英文标题】:Create a read-only/immutable copy of any object (including deep properties) 【发布时间】:2013-04-10 10:58:21 【问题描述】:

如何在 javascript 中创建一个只读/不可变版本的对象,其属性不能更改?这也应该适用于任何子对象的属性等等。

我遇到的所有执行此操作的方法(Object.definePropertyObject.freeze 等)仅适用于对象的***属性,但不适用于子对象。

(一个可能的用例:在特定模块中创建/修改settingsconfiguration 类型对象后,您需要以不可变的形式将其公开给程序的其余模块。)

【问题讨论】:

【参考方案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;

编辑: 简化了代码。现在也能正确处理数组了。

【讨论】:

很好的解决方案。我使用该解决方案在下面创建了一个代码 sn-p 来展示它的使用方式和证明。 Upvoted【参考方案2】:

对这个解决方案很感兴趣。

这里是带有示例的代码 sn-p:

/**
 * 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));

【讨论】:

以上是关于创建任何对象的只读/不可变副本(包括深层属性)的主要内容,如果未能解决你的问题,请参考以下文章

.NET ORM、不可变值对象、结构、默认构造函数和只读属性

只读(不可变)可序列化类

如何制作由映射插件创建的剔除对象的深层副本

Java中常用不可变类

java不可变类和不可变对象

js创建不可变的对象