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

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

首先来看看作用域是什么吧。

作用域

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

作为JavaScript引擎的首席检察官,他也会被自己人给骗了。


少废话来看东西

欺骗方法一 :eval()函数

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


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

猜猜运行结果是什么呢?

是1, 3

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

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

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

当console.log(..)被执行时,会在foo(..)的内部同时找到a和b但是永远也无法找到外部的b。因此会输出“1, 3”而不是正常情况下会输出的“1, 2”。

注意
严格模式的程序中,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》

你可能感兴趣的:(javascript,eval,作用域)