突破 JS 的作用域规则
会 JS 的同学都知道 JS 的作用域规则,也就是在函数和块的内部,可以访问外部的变量,比如全局变量。看起来这是一条语言铁律,但是实际上我们可以突破这一点,可以让函数无法访问到外部的变量甚至全局变量。
首先给出一段典型的代码:
var a = 1; function factory() { console.log(a); } factory();
毫无疑问,这段代码会输出1,因为factory可以访问到外部的变量。那我们可不可以通过一定的手段让 factory 无法访问到a呢,答案是肯定的,只是需要一定的技巧。
首先,我们需要明确一点,JS函数中变量的引用是在函数定义点决定的,也就是说,只要在定义函数的地方访问不到外部的变量,函数也就访问不到了。不过这听起来还是没啥用。
其次,我们知道,在JS中有个被批的很惨的关键字——with,而且还被严格模式给驱逐出境了。with 被批的原因,一在于他可以改变作用域规则,让局部变量的优先级甚至更低;二则是因为浏览器厂商痛恨他,因为有他不利于性能提升。由此可以看出,我们可以利用 with 来改变一下作用域,比如说想要屏蔽到外部的变量 a,就可以给个对象里面含有同名变量名。结合第一点,我们可以写出下面的代码:
var a = 1; var shadow = { a: 2, }; function factory() { console.log(a); } function sandbox(factory) { with (shadow) { return eval('((' + factory.toString() + ')())'); } } sandbox(factory);
上述代码会输出 2,不过你肯定想说,这有什么卵用,我只能指定某个变量,然后拿个假的值来保护原值。这是没什么卵用,但是如果配合上 ES6 的一个新特性——Proxy 就很有用了。
ES6 的 Proxy 应该是大家很少接触的一个特性,这个特性使得JS可以元编程了。结合 Proxy 可以把我们的 sanbox
函数改变得很有用处:
var a = 1; function sandbox(factory) { var shadow = new Proxy({ window: {}, factory, eval, console, }, { get(target, key, reveiver) { return Reflect.get(target, key, reveiver); }, has() { return true; } }); with (shadow) { return eval('((' + factory.toString() + ')())'); } } function factory() { console.log(a); console.log(this.a); !function () { var b = 3; console.log(b); }(); console.log(b); } sandbox(factory);
执行这个函数可以发现,外部变量甚至全局变量 a 无法被访问到了。而且由于函数的缘故,内部的作用域并没有被打乱,也就是说我们实际上获得了一个真沙盒!通过使用 sandbox,我们完完全全突破了 JS 的作用域规则。对外部变量的访问受到我们控制了,只能访问到我们所设置的 白名单 里的对象,想怎么玩就怎么玩。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 浅探 webpack 的 module
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
@slashhuang Proxy更重要啦
说到底还是with