1、再说作用域:作用域是一套规则,引擎使用这个规则来在当前作用域和嵌套的子作用域中根据标识符名称去进行变量查找。
2、作用域的两种工作模型:词法作用域和动态作用域。
3、javaScript作用域的工作模型:词法作用域。
1、词法阶段
1、什么是词法作用域:定义在词法阶段的作用域。简单的说,你的源码写出来一些变量和块的作用域就确定了,在程序编译阶段的步骤一即词法分析阶段,对字符串解析出来的词法单元的作用域延续了源码的作用域(大部分都是这样)。
2、举例:
function foo(a){
var b=a*2;
var c=a;
function bar(c){
console.log(a,b,c)
}
bar(b*3)
}
foo(2)
(1)在全局作用域中:只有一个标识符foo;
(2)在foo的作用域中:只有三个标识符a,bar,b;
(3)在bar的作用域中:只有一个标识符c;
特别注意的是:一个函数,参数的设定就相当于在函数的作用域中声明了参数这个变量,比如说function bar(c){} 这就相当于在bar作用域中已经有了对c变量的声明,即对c查询的时候,bar中会找到c。
3、作用域查找会在找到第一个匹配的标识符时停止。在2中的举例中:引擎执行console.log(a,b,c)的时候,查询a,现在bar作用域中找,没找到,到外层foo作用域中找,a是foo函数的参数,所有找到了。b和a一样。找c的时候在当前bar的作用域就找到了c,找到就停止了,即使外层foo作用域中也有c,但是内层bar中的标识符遮蔽了外层foo中的标识符,俗称遮蔽效应。
4、词法作用域只会查找一级标识符,比如说a.ccr这种东西,就只会查找a,然后对象属性访问规则会接管对ccr属性的访问。
2、欺骗词法
1、JavaScript有两种机制来在运行的时候去修改词法作用域,eval和with,两者有动态生成代码的作用。
2、这里我们不会详细说明这两种机制,因为根本不建议使用这两种机制来修改词法作用域,但我们会详细分析一下原因
3、原因一,动态生成的代码有可能会造成遮蔽效应:
function foo(str,a){'
eval(str);
console.log(a,b);
}
var b=2;
foo("var b = 3;",1);
假如没有动态生成的代码var b = 3;我们在运行console.log(a,b)的时候在foo的作用域可以找到a,但是在全局作用域才能找到b,但是使用了动态生成代码,在运行的时候,eval(str)所在的位置就有了b变量的声明,我们在foo的作用域就找到了b,全局作用域的那个b就被遮蔽掉了。这完全不是我们想要的结果!
4、原因二(最重要的原因),严重影响运行的性能!:
因为JavaScript引擎在编译阶段进行数项的优化,有些优化必须依赖根据词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在运行的时候快速找到标识符。但是用了eval和with,引擎就不知道你到底会生成什么样的代码,会对词法作用域做怎么样的修改,无法对变量和函数的定义位置进行预测和确定,运行时速度会十分慢!