Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

突破JS的作用域规则 #12

Open
renaesop opened this issue Oct 10, 2016 · 2 comments
Open

突破JS的作用域规则 #12

renaesop opened this issue Oct 10, 2016 · 2 comments

Comments

@renaesop
Copy link
Owner

会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的作用域规则。对外部变量的访问受到我们控制了,只能访问到我们所设置的“白名单”里的对象,想怎么玩就怎么玩。

@slashhuang
Copy link

说到底还是with

@renaesop
Copy link
Owner Author

@slashhuang Proxy更重要啦

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants