身份保持膜代理的用例是什么?

What would be a use case for identity-preserving membrane proxies?

当我阅读有关 ES6 代理的内容时,它似乎很容易理解,直到我看了 this example

我很难过。我不明白他们使用的 "wet/dry" 术语,而且我不知道什么时候我会遇到一个理想选择的情况,尤其是因为我似乎找不到一个。

有人可以简要说明这种情况会发生在哪种情况下吗?

首先,了解一些基础知识:对象是属性的集合(其中一些是函数,正式命名为 "methods")。这听起来很明显,但它很重要:您通过对象和 属性 名称引用其他值。

代理允许您重写通过(包含)对象的元组和 属性 名称引用其他值的规则。例如,您可以将 "private" 成员隐藏在代理后面。

但是假设你有一个循环对象引用。例如,

var x = { z: function() { throw new Error("This shouldn't be callable"); };
var X = /* proxy representing x, where X.z is hidden and cannot be called */;

var y = { x: x };
x.y = y;

或者用文档对象模型术语来说,document.documentElement.ownerDocument == document.

在普通代理中,引用 X.y 会 return y。那里真的没有错...除了 X.y.x === x 和 X.y.x !== X。所以我仍然可以调用:

X.y.x.z(); // throws new Error("This shouldn't be callable")

膜都是为了确保 非原始 属性(对象和函数)保留这种身份关系,并通过 属性 查找。膜可防止您意外地直接访问各种对象的底层(可能是本机的)实现。

如果 X 是 x 的基于膜的代理,X.y 将 而不是 return y。相反,它将 return y 的代理,我将其称为 Y。Y 公开 y 的属性,就像 X 公开 x 的属性一样。

更重要的是,假设我指的是 X.y.x:

X.y.x === X; // true
X.y.x !== x; // also true
typeof X.y.x.z // returns "undefined", not "function"
X.y.x.z(); // throws TypeError("X.y.x.z is not a function")

对 X(x 的代理)的引用是通过膜 return 编辑的。因此,身份 属性 得以保留。 (x.y.x === x, 所以 X.y.x === X.)

这是最重要的概念:膜意味着您可能永远看不到原始对象,只能看到代表它们的代理对象。

var X = (function() {
    var x = { z: function() { throw new Error("This shouldn't be callable"); };
    var y = { x: x };
    x.y = y;

    var X = /* membrane proxy representing x, where X.z is hidden and cannot be called */;
    return X;
})();

X.y.x === X; // still true

Tom van Cutsem's articles on JavaScript Membranes, one of which you cited above 中,值 x、y 和 x.z 都可以被视为 "wet" 对象图的一部分,而通过 X 或 Y 引用的任何内容都将是一部分"dry" 对象图。 (这里的术语图来自图论,它是离散数学研究的一部分。对象图是指一组相关的对象,而膜是将 "native" 对象集与代理集分开的东西对象。)

x 和 y 值无法直接从函数外部访问。 (在 JavaScript 的说法中,它们在这个例子中是局部变量,但从膜的角度来看,这在一定程度上是误导的:如果我们谈论的是 DOM 文件,那么你在网络浏览器中真正得到的是什么就像 Mozilla Firefox 是 DOM 文档的代理,而不是本地内存中的实际文档对象。无论是局部变量还是插入到 JavaScript 范围内的值,你都是 运行,膜及其代理不会关心。)

相反,您对 x、y 及其属性的唯一访问权限是通过 X 膜代理以及您从 X 获得的任何属性。因为它是一个膜,所以该访问始终是间接的。

至于会发生这种情况的场景:假设您拥有可以执行各种操作的可信代码,例如访问计算机的文件系统。您不希望网页能够直接从文件系统中读取,或者更糟的是,向其中写入。代理膜通过仅公开您打算公开的属性和方法,减少了网页可以使用的 API,因此意外访问该受信任代码的可能性大大降低。这使得安全漏洞变得更加罕见。

最重要的是,如果膜实现是正确的(这比听起来要难得多),网页 JavaScript 不知道或不关心它正在处理代理。 网页脚本认为它处理的是一个普通的DOM。这就是我们想要的。