JS 执行

变量读写

开启沙箱后,对 window 属性的读写都会局限在沙箱之内,不会外溢,包括以下操作:

window.foo = 1;
window["foo"] = 1;
Reflect.defineProperty(window, "foo", {
  value: 1,
});
delete window.foo;
Reflect.deleteProperty(window, "foo");

注意读写操作能否成功也受到原始变量属性的影响,比如 window 上原本有一个不可写(non-writable)的属性 foo,那么在沙箱中赋值也是失败的:

window.foo = "new value"; // ❌

除了 window 之外,Haploid.js 还会保护对 document 属性的读写,虽然一般很少有人会这么做。

注意

对 window、document 之外的其它全局变量的读写,则不受沙箱保护,例如 navigator.c=1

变量逃逸

你可能有这样的需求,某些变量就是要越过沙箱的限制,去读写真正 window 和 document 上的版本。在 Haploid.js 中,可以通过 escapeVariables 选项来实现。

container.registerApp({
  name: "foo",
  entry: "https://foo.com/entry",
  activeWhen: "/foo",
  sandbox: {
    escapeVariables: ["__GLOBAL_BASE"],
  },
});

不过需要特别注意的一个风险是,一旦全局变量被重新定义,很有可能导致之前能读取的变量变得不可读取。

window.foo; // ok
// other program defines foo to unreadable
window.foo; // throw error

全局变量

通常对于读写 window.foo 来说,我们可以省略上下文前缀,直接以 foo 来代替。日常我们就是这样来使用 document、history、navigaor 的。

在沙盒环境中,除了 self、top、parent、globalThis、document、location、addEventListener、removeEventListener、dispatchEvent、eval、isFinite、isNaN、parseFloat、parseInt、hasOwnProperty、decodeURI、decodeURIComponent、encodeURI、encodeURIComponent 等和在 envVariables 选项中声明的变量外,其它都不应如此使用,因为会逃逸到全局中去,而且对于未声明过的变量,在严格模式(开启了useStrict选项)下还会抛出异常。

建议访问自定义变量均使用 window/globalThis 上下文。

环境变量

Haploid.js 提供了 envVariables 选项来声明一些虚拟变量。该选项无论开启沙箱都有效,但也有明显的区别。

container.registerApp({
  name: "foo",
  entry: "https://foo.com/entry",
  activeWhen: "/foo",
  envVariables: {
    __BASE__: "https://foo.com/",
  },
});




 
 
 

在沙箱环境中,这些变量可始终被读取。在非沙箱环境,只有在入口处的同步代码中可访问。

如果与 window 上的变量冲突,那么以 envVariables 中的取值为准。

上下文传递

在沙箱中,JS 都是在独立的 function 上下文下执行的,因此如下列代码中,全局变量 a 是无法被后面访问到的:

<script>
  var a = 2022;
</script>
<script>
  console.log(a);
</script>

如果要共享变量,可以在 window 上写入。