【问题标题】:JS equivalent to Java's Collections.unmodifiableCollectionJS 等价于 Java 的 Collections.unmodifiableCollection
【发布时间】:2019-10-12 19:56:28
【问题描述】:

我经常在我的 java 代码中使用这种策略,以使 Collection 对外部世界只读,但避免大/经常克隆:

public abstract class MyClass {
    List<Long> myIds;

    public Collection<Long> getIds() {
        return Collections.unmodifiableCollection(this.myIds);
    }
}

我想在我的 JS 类中遵循相同的模式。这将使我的业务逻辑代码更安全、更清晰,因为我将能够在拥有字段的类中控制对列表的每次更改(推送/拼接等)。

目前对列表使用“私有”字段,并使用 get-set 函数从外部访问它们。唯一缺少的链接是一些等效于 java 的 Collections.unmodifiableCollection。不会复制整个列表的东西(例如 slice()),并且不会影响原始字段(例如 Object.freeze())。

JS中有这样的功能吗?如果没有,有人怎么能达到类似的效果(自定义迭代?)

【问题讨论】:

    标签: javascript readonly-collection


    【解决方案1】:

    如果您不想复制对象,并且想确保原始对象不会在外部发生变异,则一种选择是返回一个代理,当任何尝试变异时它会抛出:

    const handler = {
      get(obj, prop) {
        return obj[prop];
      },
      set() {
        throw new Error('Setting not permitted');
      }
    }
    class MyClass {
      _myIds = ['foo', 'bar']
      getIds() {
        return new Proxy(this._myIds, handler);
      }
    }
    
    const instance = new MyClass();
    const ids = instance.getIds();
    console.log(ids);
    // Error:
    // ids[2] = 'baz';
    // Error:
    // ids.push('baz');

    当然,instance._myIds 在技术上仍然是可见的——如果你想防止这种情况发生,你可以使用类似 WeakMap 的东西来确保私有数组真正只在类内部可见。

    但是代理有点慢。您可能会考虑使用 Typescript 的 ReadonlyArray 代替 - 这样,您可以确保代码在 返回 后不包含任何会改变数组的内容,同时保持实际代码在运行时快速运行:

    private myIds = ['foo', 'bar']
    public getIds() {
      return this.myIds as ReadonlyArray<string>;
    }
    

    【讨论】:

      【解决方案2】:

      如果你不限于只使用 JS 解决方案,那么你可以尝试 immutable.js (https://github.com/immutable-js/immutable-js)。

      来自文档:

      Immutable.js 提供了许多 Persistent Immutable 数据结构,包括:List、Stack、Map、OrderedMap、Set、OrderedSet 和 Record

      下面是 Immutable.List 的简单示例:

      const list1 = Immutable.List([ 1, 2 ]);
      const list2 = list1.push(3, 4, 5);
      const list3 = list2.unshift(0);
      const list4 = list1.concat(list2, list3);
      
      console.log('list1:', list1); // [1,2]
      console.log('list2:', list2); // [1,2,3,4,5]
      console.log('list3:', list3); // [0,1,2,3,4,5]
      console.log('list4:', list4); // [1,2,1,2,3,4,5,0,1,2,3,4,5]
      &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.js"&gt;&lt;/script&gt;

      更新于格林威治标准时间 2019 年 5 月 27 日星期一 09:55:43

      【讨论】:

      • 如果您能分享您的想法并告诉我您认为我的回答有什么问题,我将不胜感激。也许我误读了这个问题?请不要像那样投反对票。
      • 我不是反对者,但问题要求 JS 中的一个功能,因此首选纯 JS 解决方案。此外,您可能希望提供一些简短的代码来演示该库如何解决问题中提出的特定情况。
      • @blaz 感谢您的解释!我没有注意到需要纯 JS 解决方案。我会更新我的答案。
      • @PaulStenne 谢谢!我感谢建设性的批评。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-27
      • 1970-01-01
      • 2018-12-16
      • 1970-01-01
      • 1970-01-01
      • 2010-11-06
      • 2010-10-01
      相关资源
      最近更新 更多