前端作用域冲突之快照沙箱和代理沙箱
关于快照沙箱和代理沙箱的使用时机并没有具体明确的指导原则,它们的使用一般需要根据具体的使用背景和应用需求来决定。一般来说,快照沙箱在应用初始运行时捕获并记录其对全局变量的改动,之后在应用卸载时回退这些改动,保证不会对其他环境和应用产生影响。而代理沙箱则是利用JS的Proxy对象在应用运行时动态地隔离其全局变量操作,代理沙箱可以处理应用的动态脚本并实现一些无法通过静态分析完成的全局变量隔离操作。
快照沙箱的实现方式相对简单且易于理解,它提供了对低版本浏览器的良好支持。但使用快照沙箱可能会存在一定的内存开销,并且对应用的动态脚本处理能力有限。
而代理沙箱虽然在处理动态脚本和全局变量隔离方面有优势,但要注意的是,代理沙箱的实现依赖于JavaScript的Proxy对象,这意味着在某些不支持Proxy的低版本浏览器中,代理沙箱无法正常工作。
所以,如果你的应用需要处理大量的动态脚本,并且环境支持Proxy,那么使用代理沙箱可能会是一个好选择。相反,如果应用主要由静态脚本构成,或者需要在低版本浏览器中运行,那么快照沙箱可能更适合你的需求。
- 代理沙箱
代理沙箱是通过代理API创建一个全局对象的替代者,并通过它来控制对全局对象的访问。主要使用了Proxy对象来创建代理,Proxy对象的作用是,在对象之间创建一个代理,该代理可以监视并修改对原始对象的访问。
// 原始全局对象
const globalContext = window;
// 创建 target 对象
const target = Object.create(null);
// 创建代理
const proxy = new Proxy(target, {get: (target, p) => {// hijack global accessingif (p === 'window' || p === 'self' || p === 'globalThis') {return proxy;}// 返回原始全局对象的属性值return globalContext[p];},set: (target, p, value) => {// 修改代理对象的属性值,不影响原始全局对象target[p] = value;return true;}
});
在这段代码中,通过代理控制访问到全局对象的所有操作。例如:
当试图获取 globalContext 上的某个属性时(例如,globalContext.window),将返回被代理的对象(代理沙箱);
当试图设置 globalContext 上的某个属性时,它将修改并返回代理对象,保持全局对象不变。
- 快照沙箱
快照沙箱主要思路是,一开始就拍摄全局状态的快照,然后在代码执行过程中任何对全局状态的修改只会影响当前沙箱环境,不会泄露到全局环境。
// 创建代理对象
class SnapshotSandbox {constructor() {// 保存环境快照this.snapshot = this.recordSnapshot();}// 记录全局状态快照recordSnapshot() {return new Map(Object.getOwnPropertyNames(window).map(name => [name, window[name]]));}// 通过快照还原全局状态restoreSnapshot() {for (const [name, value] of this.snapshot.entries()) {if (!(value === window[name])) {window[name] = value;}}}
}
在代码中,我们首先创建一个类SnapshotSandbox。在构造函数中,我们记录了window对象初始状态的快照。
记录快照的方法是通过Object.getOwnPropertyNames(window)得到全局对象上所有自有属性的键,然后每个键和对应的初始值一起保存在 Map 中。
当我们受够了在沙箱环境中的修改后,可以通过调用restoreSnapshot()方法把全局环境恢复到初始状态,也就是我们保存的全局环境快照。
以上就是文章全部内容了,如果喜欢这篇文章的话,还希望三连支持一下,感谢!