作用域与词法作用域
作用域
我们先抛出一个概念:“词法作用域是作用域的一种工作模型”,先不管这句话的深层次的意思,就但看表面,我们就应该可以得出一个结论,那就是没有作用域的概念就没有词法作用域的概念。所以…接下来,你懂的…
什么是作用域
作用域就是一套规则,用于确定在何处以及如何查找变量(标识符)的规则”。在这句话中读到一个关键点 查找变量(标识符),那么就从查找变量说起吧。
先看一段及其简单的代码
function foo() {
var a = 'iceman';
console.log(a); // 输出"iceman"
}
foo();
在foo函数执行的时候,输出一个a变量,此时的变量a是从函数内部的声明 var a = ‘iceman’
此时就是在函数作用域中找到了a变量
再来一段代码
var b = 'programmer';
function foo() {
console.log(b); // 输出"programmer"
}
foo();
同样的道理,在输出b的时候,自己函数内部没有找到变量b,那么就在外层的全局中查找,找到了就停止查找并输出了。
此时就是在全局作业域中找到了b变量
**通俗的讲:**作用域就是查找变量的地方,在函数中找到变量,就可以说是在该函数作用域中找到了该变量;在全局中找到该变量,就可以说在全局作业域中找到了该变量。
作用域嵌套
在还没有接触到ES6的let、const之前,只有函数作用域和全局作用域,函数作用域肯定是在全局作用域里面的,而函数作用域中又可以继续嵌套函数作用域,如图:
用代码表示:
以上两张图可以很直观的看出作用域的嵌套关系了吧。查找变量也是顺着红色的箭头走的,从里到外,这从里到外的各层作用域就组成了作用域链。
词法作业域
“词法作用域是作用域的一种工作模型”,作用域有两种工作模型,在JavaScript中的词法作用域是比较主流的一种,另一种动态作用域(比较少的语言在用。
**概念:**所谓的词法作用域就是在你写代码时将变量和块作用域写在哪里来决定,也就是词法作用域是静态的作用域,在你书写代码时就确定了。
请看以下代码:
function fn1(x) {
var y = x + 4;
function fn2(z) {
console.log(x, y, z);
}
fn2(y * 5);
}
fn1(6); // 6 10 50
这个例子中有个三个嵌套的作用域,如图:
-
A 为全局作用域,有一个标识符:fn1
-
B 为fn1所创建的作用域,有三个标识符:x、y、fn2
-
C为fn2所创建的作用域,有一个标识符:z
作用域是由代码写在哪里决定的,并且是逐级包含的。
在此强调,词法作用域就是作用域是由书写代码时函数声明的位置来决定的。编译阶段就能够知道全部标识符在哪里以及是如何声明的,所以词法作用域是静态的作用域,也就是词法作用域能够预测在执行代码的过程中如何查找标识符。
注1:eval()和with可以通过其特殊性用来“欺骗”词法作用域,不过正常情况下都不建议使用,会产生性能问题。
注2:ES6中有了let、const就有了块级作用域,后面会专门介绍。