一、什么是作用域
所谓的作用域,实际上就是指程序代码中定义变量所在的区域。
作用域又决定了如何来查找变量,也就是说作用域决定了当前执行代码对变量的访问权限。
二、作用域的模式
作用域模式可分为两种,分别是词法/静态作用域(lexical scoping);与之对应的则是动态作用域。
其中,词法/静态作用域包含函数作用域、以及块作用域。
词法作用域,指的是变量作用域的定义由代码书写阶段决定。
动态作用域,指的是变量作用域的定义由代码执行阶段决定。
如图所示:
我们来看如下代码来对两种作用域模式进行分析:
var num = 100; function test(){ var num = 1; print(); } function print() { console.log(num); } test() // 结果如何呢???
我们来分析一下
假设JavaScript采用的是词法/静态作用域,实际的执行过程应该如下:
首先执行 test 函数,再执行 print 函数,从 print 函数内部查找是否有局部变量 num,如果没有,就根据代码书写的位置,查找上层代码,也就是 num 等于 100,所以结果会打印 100。
假设JavaScript采用的是动态作用域,实际的执行过程应该如下:
首先执行 test 函数,再执行 print 函数,从 print 函数内部查找是否有局部变量 num,如果没有,就从调用 print 函数的 test 函数内部查找,也就是 num 等于 1,所有打印结果是 1。
通过编译执行以上代码,我们发现最终的执行打印的结果为 100,也就说明 num 作用域的定义发生在代码书写阶段。
虽然说有两种作用域模式,实际上在JavaScript中,并不存在动态作用域,它只有词法作用域,但是this的这种机制,在某种程度上和动态作用域有相似之处。
三、块作用域
ES6 引入了 let 和 const 关键字后,JavaScript 也就有了块级作用域。简单的来说,除函数以外,任何由 {} 所定义的代码块,都具有块作用域的特性。
1、普通代码块
{ let i = 10; } console.log(i); // 报错:i is not defined 无法输出打印
2、逻辑代码块
if (true) { let a = 10; } console.log(a); // 报错:a is not defined 无法输出打印 for (var i = 0; i < 3; i++) { let k = 10; } console.log(k); // 报错:k is not defined 无法输出打印
除以上两种情况以外,还包含,switch、while、do while、try catch 等
四、函数作用域
JavaScript中,在函数内部声明的变量只能影响到变量所在函数体本身,无法从外部对函数内部的变量进行调用,被称为函数作用域;
简单的说,函数作用域就是由函数代码块产生的
function test () { let num = 10; } test() console.log(num); // 报错:num is not defined 无法输出打印
五、总结
1、作用域工作模式:词法/静态作用域、动态作用域
2、词法/静态作用域:函数作用域、块作用域
3、JavaScript 没有动态作用域
4、词法/静态作用域的本质在于代码块在何处声明
5、动态作用域的本质在于代码块在何处调用
6、词法/静态作用域链基于作用域嵌套
7、动态作用域链基于调用栈