当我阅读有关 ES6 代理的内容时,它似乎很容易理解,直到我查看了这个示例。
我难住了。我不明白他们使用的“湿/干”术语,而且我不知道什么时候会出现这种情况,这将是理想的选择,尤其是因为我似乎找不到一个。
有人可以简要解释一下会发生这种情况的情况吗?
当我阅读有关 ES6 代理的内容时,它似乎很容易理解,直到我查看了这个示例。
我难住了。我不明白他们使用的“湿/干”术语,而且我不知道什么时候会出现这种情况,这将是理想的选择,尤其是因为我似乎找不到一个。
有人可以简要解释一下会发生这种情况的情况吗?
首先,一些基础知识:对象是属性的集合(其中一些是函数,正式命名为“方法”)。这听起来很明显,但很重要:您可以通过对象和属性名称来引用其他值。
代理允许您重写规则以通过(包含)对象的元组和属性名称来引用其他值。例如,您可以将“私人”成员隐藏在代理后面。
但是假设你有一个循环对象引用。例如,
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。
在普通代理中,引用 Xy 将返回 y。那里没有什么问题...除了 Xyx === x 和 Xyx !== X。所以我仍然可以调用:
X.y.x.z(); // throws new Error("This shouldn't be callable")
膜就是要确保非原始属性(对象和函数)保持这种身份关系,并通过任何属性查找组合来维护您拥有的任何代理规则。膜可以防止您意外地直接访问各种对象的底层(可能是本机)实现。
如果 X 是 x 的基于膜的代理,则 Xy不会返回 y。相反,它将返回 y 的代理,我将其称为 Y。Y 公开 y 的属性,就像 X 如何公开 x 的属性一样。
更重要的是,假设我指的是 Xyx:
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 的代理)的引用。因此,身份属性得以保留。(xyx === x,所以 Xyx === 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 的关于 JavaScript 膜的文章(您在上面引用的其中一篇)中,值 x、y 和 xz 都可以被视为“湿”对象图的一部分,而通过 X 或 Y 引用的任何内容都将是“干”对象图。(这里的术语图来自图论,是离散数学研究的一部分。对象图是指一组相关的对象,而膜是将“本地”对象集与这些对象的代理集分开的东西。 )
x 和 y 值不能直接从函数外部访问。(用 JavaScript 的说法,在这个例子中它们是局部变量,但从膜的角度来看,这在一定程度上会产生误导:如果我们谈论的是 DOM 文档,那么你在 Mozilla Firefox 等网络浏览器中真正得到的是DOM 文档,而不是本地内存中的实际文档对象。无论是本地变量还是插入到您正在运行的 JavaScript 范围内的值,膜及其代理都不会关心。)
相反,您对 x、y 及其属性的唯一访问权限是通过 X 膜代理以及您从 X 获得的任何属性。因为它是膜,所以访问始终是间接的。
至于会发生这种情况的场景:假设您拥有可以执行各种操作的受信任代码,例如访问计算机的文件系统。您不希望网页能够直接从文件系统读取,或者更糟的是,写入文件系统。代理膜通过仅公开您打算公开的属性和方法,减少了网页可以使用的 API,因此意外访问该受信任代码的可能性大大降低。那么,这使得安全漏洞变得更加罕见。
最重要的是,如果膜实现是正确的(这比听起来要困难得多),网页 JavaScript 不知道也不关心它正在处理代理。 网页脚本认为它正在处理一个普通的 DOM。这就是我们想要的。