JS进阶-作用域-词法作用域和动态作用域
词法作用域
在介绍作用域的第一篇文章中说到编译器的第一个阶段是分词,词法作用域就是定义分词阶段的作用域,是由代码中变量作用域和块作用域的位置决定的,所以词法分析器在处理代码时会保持作用域不变。
词法作用域只由函数被声明时所处的位置决定。
function foo(a) {
var b = a * 2;
function foo2(c) {
console.log(a,b,c)
}
foo2(b * 3);
}
foo(1); // 1 2 6
上面的例子中有三个逐级嵌套的作用域:
- 第一级就是最外层的全局作用域,只有一个foo标识符。
- 第二级是foo创建的作用域,包含a、b和foo2三个标识符。
- 第三级时foo2创建的作用域,只有一个c标识符。
作用域的嵌套结构和位置关系为JS引擎提供了查找标识符的信息。在上面的示例中,引擎要执行console.log(...)
语句,并对a、b和c这3个变量引用,它首先要会从最内部的foo2作用域中查找,引擎找不到a,就会向上一级的foo作用域中查找,找到后对变量a进行了引用。b变量也是在foo作用域中找到。c变量在foo2作用域中找到。
多层嵌套的作用域中可以定义同名标识符,内部的标识符会遮蔽外部的标识符,被称作“遮蔽效应”。
var a = 1;
function foo() {
var a = 2;
console.log(a); // 2
}
foo();
全局变量会自动成为全局对象的属性,所以可以间接的通过引用全局对象的属性访问该变量。
var a = 1;
function foo() {
var a = 2;
console.log(window.a); // 1
}
foo();
动态作用域
JavaScript使用的是词法作用域,词法作用域的特点是它是在代码书写阶段定义的。动态作用域的特点是它是在代码运行时确定的,解释动态作用域是为了理解后面要说的this机制。
动态作用域只由函数被调用时所处的位置决定。
var a = 1;
function foo() {
console.log(a);
}
function foo2() {
var a = 2;
foo();
}
foo2(); // 1
-
如果处于词法作用域,变量a会先在foo函数中查找,没有找到。然后沿着作用域链到全局作用域中查找,找到了把1赋值给a,最终控制台打印1。
-
如果处于动态作用域,变量a同样先在foo函数中查找,没有找到。然后顺着调用栈到foo2中查找,找到了把2赋值给a,最终控制台打印2。
总结一下就是,词法作用域是由函数声明位置决定,动态作用域由函数调用位置决定。
胖胖熊笔记,笔记已迁移