JavaScript 做用域之eval()欺骗词法做用域

最近在读凯尔辛普森的《你不知道的JavaScript》,感受挺有意思的,在理解做用域以后,看到了一个有意思的东西:欺骗词法做用域。编程

首先来看看做用域是什么吧。编程语言

做用域

简而言之就是一套储存变量并规定如何访问并修改变量的规则,是几乎全部编程语言最基本的功能之一。

做为JavaScript引擎的首席检察官,他也会被本身人给骗了。函数


少废话来看东西

欺骗方法一 :eval()函数

原理:JavaScript中的eval(str)函数能够接受一个字符串为参数,并将字符串内容视为好像在书写时就存在于eval()函数所在位置的代码。spa


function foo(str,a){
    eval(str); //欺骗代码               
    console.log(a,b);
 }
 var b = 2;
 foo("var b = 3;" , 1);

猜猜运行结果是什么呢?code

是1, 3blog

乍一看,foo()中console.log(a,b)里的b会会去外层访问咱们定义的全局变量b,做用域也是这么想的,因此他只是一如往常的去检查。ip

可是在执行eval(..)以后的代码时,引擎并不“知道”或“在乎”前面的代码是以动态形式插入进来。作用域

eval(..)调用中的"var b = 3; "这段代码会被看成原本就在那里同样来处理。因为那段代码声明了一个新的变量b,所以它对已经存在的foo(..)的词法做用域进行了修改。事实上,和前面提到的原理同样,这段代码实际上在foo(..)内部建立了一个变量b,并遮蔽了外部(全局)做用域中的同名变量。字符串

当console.log(..)被执行时,会在foo(..)的内部同时找到a和b可是永远也没法找到外部的b。所以会输出“1, 3”而不是正常状况下会输出的“1, 2”。it

注意
严格模式的程序中,eval(..)在运行时有其本身的词法做用域,意味着其中的声明没法修改所在的做用域。
以下:
 function foo(str) {
 "use strict";
 eval(str);
 console.log(a);
// ReferenceError: a is not defined
    }
    foo("var a = 2");`

其中eval(str);运行时没法修改所在的做用域了,此时a便没法找到。

JavaScript中还有其余一些功能效果和eval(..)很类似。setTimeout(..)和setInterval(..)的第一个参数能够是字符串,字符串的内容能够被解释为一段动态生成的函数代码。这些功能已通过时且并不被提倡。不要使用它们!

词法做用域的"欺骗",eval()只是其中一个,更多详情推荐看看凯尔辛普森的《你不知道的JavaScript》,他会带你深刻的了解JavaScript。

凯尔辛普森的《你不知道的JavaScript》
相关文章
相关标签/搜索