JS进阶-作用域-词法作用域和动态作用域

词法作用域

在介绍作用域的第一篇文章中说到编译器的第一个阶段是分词,词法作用域就是定义分词阶段的作用域,是由代码中变量作用域和块作用域的位置决定的,所以词法分析器在处理代码时会保持作用域不变。

词法作用域只由函数被声明时所处的位置决定。

function foo(a) {
  var b = a * 2;
  function foo2(c) {
    console.log(a,b,c)
  }
  foo2(b * 3);
}
foo(1); // 1 2 6

上面的例子中有三个逐级嵌套的作用域:

  1. 第一级就是最外层的全局作用域,只有一个foo标识符。
  2. 第二级是foo创建的作用域,包含a、b和foo2三个标识符。
  3. 第三级时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
  1. 如果处于词法作用域,变量a会先在foo函数中查找,没有找到。然后沿着作用域链到全局作用域中查找,找到了把1赋值给a,最终控制台打印1。

  2. 如果处于动态作用域,变量a同样先在foo函数中查找,没有找到。然后顺着调用栈到foo2中查找,找到了把2赋值给a,最终控制台打印2。

总结一下就是,词法作用域是由函数声明位置决定,动态作用域由函数调用位置决定。

posted @ 2021-09-29 11:12  wmui  阅读(92)  评论(0编辑  收藏  举报