词法作用域
首先,先来说一下词法作用域: 词法作用域就是定义在词法阶段的作用域。用更容易理解的话来说, 词法作用域就是由你在写代码时将块变量和块作用域写在哪里来决定的
if(true){ let b = 1 }
如代码所示,在if里面声明了一个变量b,那么b的词法作用域就在if的块区域之中。变量声明'出生'在哪,那么它的词法作用域就是在哪。
欺骗词法作用域
如上所述,词法作用域就是由你在写代码时将块变量和块作用域写在哪里来决定的,但是Javascript中存在两个机制可以去欺骗词法作用域,它们违背了词法作用域的规则,所以我们称之为欺骗词法作用域。
1.eval()
eval()可以接受一个字符串作为参数,它将原本不属于此处的代码搬到此处,让代码执行的就像天生就定义在此处一样
function foo(str, a){ eval(str) console.log(a,b) } foo('var b = 3', 1) //输出结果为 1 3 其实,这段代码就等同于 js 代码解读 复制代码 function foo(str, a){ var b = 3 //eval(str) console.log(a, b) } foo('var b = 3', 1)
原本'var b = 3'是在全局作用域上,但eval()函数将之搬入函数foo()作用域中,使其成为了foo里面的代码,违背了词法作用域的规则,所以称为欺骗词法。
2.with
with()一般用来批量的去修改一个对象中的值,我们这里来说说with()泄露属性值,以此来欺骗词法作用域
function foo(obj){ with(obj){ a = 123 } } var o1 = { a: 555 } var o2 = { b: 555 } foo(o1.a) console.log(o1.a)//输出结果为 123 foo(o2.b) console.log(o2.b)//输出结果为 555 console.log(a) //输出结果为123
如同,我们可以看到,我们访问a的结果为123。在全局作用域中,a并没有被声明,由此可见,with()这个语句,把a这个属性泄露到了全局作用域上面。原因是因为当with()修改对象中不存在的属性时,会将该属性直接泄露到全局作用域。所以 a = 123 相当于写在了全局中