javascript只用静态作用域(词法作用域),函数作用域在定义时确定,变量查找沿代码书写结构向上进行,与调用位置无关;动态作用域则依赖调用链查找,js并不采用。

JavaScript 只用静态作用域(也就是词法作用域),它不支持动态作用域。理解两者的区别,关键不在“JS有没有”,而在于“为什么JS选静态、其他语言怎么选、以及错误认知从哪来”。
静态作用域:看函数写在哪,不看谁调用它
函数内部访问变量时,JS 会沿着代码书写结构向上查找——从函数定义的位置开始,一层层往外找,直到全局。这个查找路径在你写完代码那一刻就固定了,跟运行时谁调用了它完全无关。
-
例子:函数
test()定义在全局,里面读val,那它永远查全局的val,哪怕被bar()(内部有同名var val = 2)调用,也还是输出1 - 核心判断法:把函数剪下来,贴到别的地方执行,只要没改它的上下文结构,结果不变——这就是静态作用域的体现
动态作用域:看函数被谁调用,临时决定查哪
这不是 JavaScript 的行为,但有助于反向理解静态作用域。动态作用域下,函数执行时会顺着“调用链”向上找变量:谁调了我,我就去它的作用域里翻;如果没找到,再往上推,直到全局。
-
对比例子:同样
test()被bar()调用,若 JS 是动态作用域,test()就会看到bar()内部的val = 2,输出2 - 现实中用它的语言:Bash、Perl(部分模式)、Emacs Lisp。它们适合写 shell 脚本或宏扩展,需要“继承调用者环境”的灵活性
为什么容易混淆?三个常见误解点
很多人误以为 JS 有动态作用域,往往卡在这几个地方:
-
this 不是作用域:
this的值确实由调用方式决定(如obj.fn()vsfn()),但它和变量查找无关。作用域管var/let/const,this是执行上下文的独立属性 - 闭包不是动态作用域:内层函数能记住外层变量,是因为它在定义时就捕获了词法环境,不是因为“调用时回溯调用栈”
-
eval 和 with 是例外,但不改变本质:它们会临时修改作用域链,属于危险特性,ES5 严格模式已禁用
with,eval也极少用——不能拿边缘情况否定整个语言的设计原则
怎么一眼判断一个现象属哪类?
遇到变量访问问题,直接问自己两个问题:
- 这个变量声明写在函数的上面、同一级、还是外面?→ 答案指向静态作用域路径
- 如果我把这个函数单独复制出来,在另一个文件里直接运行,它还能访问到那个变量吗?→ 能,就是静态;不能,才可能是动态(但 JS 中基本不会发生)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











