JavaScrip作用域链

引子

JavaScript 作用域是一个很容易产生误解的特性,很多时候我们会因为对作用域链的了解不够充分而导致对代码运行结果的误判,下面我会用几个例子说明 JavaScript 的作用域链。

基础理论

JavaScript 的作用域时词法作用域,什么是词法作用域,我们先看看 wiki 上的定义:

静态作用域又叫做词法作用域,采用词法作用域的变量叫词法变量。词法变量有一个在编译时静态确定的作用域。词法变量的作用域可以是一个函数或一段代码,该变量在这段代码区域内可见(visibility);在这段区域以外该变量不可见(或无法访问)。词法作用域里,取变量的值时,会检查函数定义时的文本环境,捕捉函数定义时对该变量的绑定。

简单来说,词法作用域就是定义在词法阶段的作用域,词法作用域是由你在写代码时将变量和块作用域写在哪里决定的。

例子

我们来看几个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a = 1;

function fn1() {
function fn2() {
console.log(a);
}

function fn3() {
var a = 4;
fn2();
}
var a = 2;
return fn3;
}
var fn = fn1();
fn(); //输出多少

这段代码的输出结果为2,因为fn2中没有a,所以我们在fn2的上层作用域中查找,fn2的上层作用域是fn1fn1中的a2,所以console.log(a)打印的结果为2

再看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var a = 1;

function fn1() {
function fn3() {
function fn2() {
console.log(a);
}
fn2();
var a = 4;
}
var a = 2;
return fn3;
}
var fn = fn1();
fn(); //输出多少

上面这段代码的结果是undefined,这里要结合声明提前的概念分析,声明提前后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a = 1;

function fn1() {
function fn3() {
function fn2() {
console.log(a);
}
var a; //声明提前
fn2();
a = 4;
}
var a = 2;
return fn3;
}
var fn = fn1();
fn(); //输出多少

fn2的上层作用域为fn3fn3中,fn2在执行前a已声明但为赋值,所以输出结果为undefined

总结

我们可以总结如下规律:

  1. 函数在执行的过程中,先从自己内部找变量
  2. 如果找不到,再从创建当前函数所在的作用域去找, 以此往上
  3. 注意找的是变量的当前的状态
作者

bert_cai

发布于

2019-05-27

更新于

2020-11-16

许可协议

评论