如何在Javascript中使用共享的、不可变的引用类型来跟踪更改

How to track changes with shared, immutable reference types in Javascript

本文关键字:不可变 引用类型 跟踪 共享 Javascript      更新时间:2023-09-26

考虑以下示例:

function add(x, y) { return x + y; }
var collection = Object.freeze([1, 2, 3, 4]);
var consumerA = collection; // expects steady data
var consumerB = collection; // requires the latest data
var updatedCollection = collection.concat(5);
consumerA.reduce(add, 0); // 10 (desired result)
consumerB.reduce(add, 0); // 10 (incorrect result, should be 15)

consumerA使用它所期望的不可变数据进行操作。在Javascript中可以做些什么来确保consumerB始终访问最新的数据?

请注意:仅仅深度复制consumerA并将collection视为可变数据是不可行的。

UPDATE:该示例仅用于说明由共享引用类型引起的根本问题:一些使用者(或引用持有者)依赖于不可变的数据,另一些则依赖于可变的数据。我正在寻找一种适当的更改跟踪机制,在不破坏不可变数据的好处的情况下解决这个问题。

也许"变化跟踪"这个词太模糊了。关于变化跟踪,我指的是consumerB被告知变化(推动机制)或(更有趣的)能够发现变化(拉动机制)的一种方式。后者将要求consumerB以某种方式访问更新的集合。

在声明collection时使用Object.freeze,因此不能向collection添加属性。

创建consumerB时,执行对象collection 的副本

var consumerB = collection; 

因此,不能像collection那样向consumerB添加属性。

你需要克隆对象而不是复制它。你可以这样做:

var consumerB = JSON.parse(JSON.stringify(collection)); 

好吧,这是我唯一的解决方案,但可能还有其他解决方案。我将我的不可变集合封装在一个可变对象中。需要常量数据的使用者拥有对集合本身的引用。需要当前状态的使用者持有对包装器的引用。为了避免克隆,我使用了一种原始形式的结构共享:

function add(x, y) { return x + y; }
var collection = Object.freeze([1, 2, 3, 4]);
var atom = {state: collection};
var consumerA = collection;
var consumerB = atom;
console.log(consumerA === consumerB.state); // true (obviously)
// naive structural sharing to avoid cloning
atom.state = Object.create(atom.state, {length: {value: atom.state.length, writable: true}});
atom.state.push(5);
Object.freeze(atom.state);
// as desired
console.log(consumerA.reduce(add, 0)); // 10
console.log(consumerB.state.reduce(add, 0)); // 15
// structural sharing is used
console.log(Object.getPrototypeOf(consumerB.state) === collection); // true
// object comparison simply by reference check
console.log(consumerA === consumerB.state); // false

通过将不可变集合包装在可变包装器中,它就成为了一种持久数据类型。这意味着它可以被视为一个普通的、可变的对象,但保留其以前的版本,因此是持久的。顺便说一句,将包装器命名为atom并不是偶然的,而是对Clojure中相应数据类型的引用。

请注意:使用原型系统进行结构共享可能会导致内存泄漏,应谨慎使用。