javascript笔记之理解静态作用域

什么是作用域

【维基百科】作用域(scope)是指是名字(name)与实体(entity)的绑定(binding)保持有效的那部分计算机程序。作用域类别影响变量的绑定方式,根据语言使用静态作用域还是动态作用域,变量的取值可能会有不同的结果。

通俗点说,就是作用域规定了如何查找变量。

静态作用和动态作用域的概念

关于静态作用域和动态作用于在维基百科中是这样介绍的:

静态作用域又叫做词法作用域,采用词法作用域的变量叫词法变量。词法变量有一个在编译时静态确定的作用域。词法变量的作用域可以是一个函数或一段代码,该变量在这段代码区域内可见(visibility);在这段区域以外该变量不可见(或无法访问)。词法作用域里,取变量的值时,会检查函数定义时的文本环境,捕捉函数定义时对该变量的绑定。大多数现在程序设计语言都是采用静态作用域规则,如C/C++、C#、Python、Java、JavaScript……
相反,采用动态作用域的变量叫做动态变量。只要程序正在执行定义了动态变量的代码段,那么在这段时间内,该变量一直存在;代码段执行结束,该变量便消失。这意味着如果有个函数f,里面调用了函数g,那么在执行g的时候,f里的所有局部变量都会被g访问到。而在静态作用域的情况下,g不能访问f的变量。动态作用域里,取变量的值时,会由内向外逐层检查函数的调用链,并打印第一次遇到的那个绑定的值。显然,最外层的绑定即是全局状态下的那个值。采用动态作用域的语言有Pascal、Emacs Lisp、Common Lisp(兼有静态作用域)、Perl(兼有静态作用域)。C/C++是静态作用域语言,但在宏中用到的名字,也是动态作用域。

这么长一大段到底再说什么?总结一下就是一句话,两个点:

动态作用域和静态作用域,决定的是作用域链的序列

  1. 静态作用域中,变量的引用在编译的阶段就可以确定,跟程序执行的顺序无关。
1
2
3
4
5
6
7
8
9
10
// 注:以下是伪代码,不针对任何一种语言。
a=2;
function foo(){
print(a); // 这里输出2,而不是3
}
function bar(){
a=3;
foo();
}
bar();
  1. 动态作用域中,变量引用需要在程序运行时才能确定。
1
2
3
4
5
6
7
8
9
10
// 注:以下是伪代码,不针对任何一种语言。
a=2;
function foo(){
print(a); // 这里输出3,而不是2
}
function bar(){
a=3;
foo();
}
bar(); // 2

JavaScript中的静态作用域

JavaScript 采用的是静态作用域注意:with和eval的语义无法仅通过静态技术实现,实际上,只能说JS的作用域机制非常接近lexical scope。),即使一个函数定义的地方和使用的地方会相隔十万八千里,但是函数执行时,它能访问哪些变量,不能访问哪些变量,是由函数定义时的函数的作用域决定的,而函数的作用域是由定义时的位置决定的。

1
2
3
4
5
6
7
8
9
var a=2;
function foo(){
console.log(a);
}
function bar(){
var a=3;
foo();
}
bar(); // 输出2

通过分析可知,执行 foo 函数:

  • 先从 foo 函数是否有局部变量a
  • 没有找到为a的局部变量
  • 在函数作用域上,依次查找上面一层,找到a等于 2,
  • 输出 2

测试

下面是《JavaScript权威指南》里的代码,看下两个函数的执行结果是多少?

1
2
3
4
5
6
7
8
9
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
1
2
3
4
5
6
7
8
9
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();

答案:local scope

JavaScript 函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的。嵌套的函数 f() 定义在这个作用域链里,其中的变量 scope 一定是局部变量,不管何时何地执行函数 f(),这种绑定在执行 f() 时依然有效。 – 《JavaScript权威指南》

参考

javascript的词法作用域
动态作用域和词法域的区别是什么?