阅读 758

欺骗词法 eval 和 with

欺骗词法 eval()

如果说词法作用域完全由写代码期间函数所声明的位置来定义,怎样才能在运行时来修改(也可以说欺骗)词法作用域呢?

词法作用域

什么叫做词法作用域? 在编写代码时,将变量和作用域写在哪里决定的就叫做词法作用域,当词法分析器处理代码时会保持作用域不变(大部分是这样的)还有一种叫动态作用域,也就是在运行时就被动态确定的形式。在动态作用域中我们不关心函数和作用域时如何声明的,只关心他们从何处调用。

在javaScript中有两种机制来达到这个目的(eval和with),但是在代码里使用这种机制并不是什么好主意,因为欺骗词法会导致性能下降。

在JavaScript中的eval()函数可以接受一个字符串为参数,并将其中的内容视为好像在书写的时候就存在这个作用域里面了一样。下面用一个例子来说明eval():

function foo(str,b){
eval(str);
console.log(a,b);   //1  3
}
var a=2;
foo('var a=1;',3);
复制代码

在这个代码中foo()接受了一个字符串和数值做参数,其中foo中将str传入eval()函数,所以接收到的字符串“var a=1”就像是本来就存在这个作用域里的一样。所以当要输出的时候a的时候不会调用var=2这个值,而是找函数体里面的a=1因为函数体里找变量是从里向外面寻找的。

但在严格模式下是无法使用的,而且有性能上的问题

function foo(str,b){
"use strict"
eval(str);
console.log(a,b);   //2 3
}
var a=2;
foo('var a=1;',3);
复制代码

在严格模式下,eval()有自己的作用域,意味着其中的声明无法去修改当前所在的作用域。

欺骗词法with()

with通常被当作重复引用同一个对象的多个属性的快捷方式,可以不需要重复引用对象本身

var obj={
a:1
b:2
c:3
}
复制代码

当我们想要把a属性变为值为2,b属性变为值为3,c属性变为值为4。一般的方法是使用对象一个个去访问对象里的属性。看下图:

obj.a=2;
obj.b=3;
obj.c=4;
复制代码

但我们使用with就可以很快捷的改变对象属性值:

with(obj){
a=2;
b=3;
c=4;
}
复制代码

但是实际上不仅仅是为了方便访问对象属性。看看下面的代码

function foo(obj){
with(obj){
a=2;
 }
}
var o1={
a:3
}
var o2={
b:3
}
foo(o1);
console.log(o1.a)  //2
foo(o2);
console.log(o2.a)  //undefined
console.log(a)  //2   a被泄露到了全局作用域上了!

所有代码均引用书上的代码!!!
复制代码

在这个案例中创建了o1和o2两个对象,其中o1有a属性,o2没有a属性。foo(...) 函数接受了一个obj参数。该参数是对一个对象引用,并对这个对象引用执行了with(obj){...},在with内部,看似是对变量a的引用,实际上就是对属性a进行赋值(引用LHS:查找的目的是对变量进行赋值),将2赋值给a。

对o2对象执行时,o2并没有a属性,因此不会创建这个属性,o2.a保持undefined。

with可以将一个没有或有多个属性的对象处理为一个完全隔离的词法作用域,因此这个对象的属性也会被处理定义在这个歌作用域的词法标识符。

但在o2作用域,foo(...)的作用域和全局作用域都没有找到a标识符,因此a=2时,创建了一个全局变量。

文章分类
前端
文章标签