沙箱含义:应用:在JavaScript中,不同作用域可以通过作用域链进行访问(如闭包),而且通过这种方式,所有作用域都能访问到全局对象。要实现沙箱的隔离效果,需要把代码运行在一个独立的作用域,限制它对其他作用域以及全局对象的访问。例子如下,在沙箱内变量赋值不会影响沙箱外的对象,这是createSandbox函数需要实现的基本功能。一、代码执行在JavaScript中,动态执行代码的方法有Function和eval。二者对比:代码中永远不要使用eval选择Function函数来动态执行代码后,我们可以得出以下实现:二、作用域固定虽然使用Function可以让沙箱代码固定在全局作用域中执行,但我们还是需要限制代码访问全局对象。我们可以使用Object.create()来创建一个全局对象的副本,然后使用with让沙箱代码访问这个副本对象,从而达到隔离的效果。with语句可以将某个对象添加到作用域链的顶部。三、全局对象代理使用with的方式还存在一个问题,就是当访问全局对象不存在的字段时,还是能够访问到全局对象,比如新增字段。使用Proxy代理with对象中的has方法,让每次判断都返回true,这样就能真正阻断代码通过作用域链访问全局对象。with语句是通过in运算符来判定访问的字段是否在对象中,从而决定是否继续通过作用域链往上找。四、特殊处理上述代码虽然实现了基本的沙箱功能,但显然还存在一些问题:浏览器还会对一些内置函数进行保护,比如alert和setTimeout等,这些函数必须运行在window作用域下。需要把函数的作用域修改回window。这些函数都有个特点就是都是非构造函数,不能new,没有prototype属性,我们可以用这个特点来进行过滤。五、最终代码最终createSandbox函数的实现如下:六、使用效果可以在同一页面中同时运行vue2和vue3的代码,互不冲突。demo:codesandbox.io/s/clever...其他存在问题(暂时无解)冷知识Symbol.unscopables这两个例子主要的变化是在原型链上增加了一个属性,导致with语句可以成功从对象中查找到字段,实际输出的是props.props.length,导致输出和预期变得不一样。可以想想ES6对数组原型新增了多少方法,每个方法都可能导致这个问题。虽然这个代码看起来写法比较有歧义,但确实出现在了当时比较流行的框架EXT中。为了兼容这种情况,TC39加了一条规则。Symbol.unscopables的引入,唯一作用就是解决with执行环境下的历史问题。TC39把ES6以后新加数组原型链方法全部加到Array.prototype[Symbol.unscopables]中,包括includes、keys和values等等。