JavaScript中类似动态作用域的诡异行为是否为语言设计?
这是JavaScript词法作用域下的正常行为,并非理解误区
你观察到的现象其实是**JavaScript词法作用域(Lexical Scoping)**搭配可变变量的自然结果,和你误以为的“动态作用域”有本质区别,我来帮你拆解清楚:
核心原因:你访问的是可变变量,而非固定的函数引用
你的代码里,dynamic_scoped_function 中调用的 vairable_function(注意你拼写错了,应该是 variable_function)是一个在外部作用域(test函数的作用域)中声明的可变变量。JavaScript的词法作用域规则是:函数会记住它定义时所在的作用域,但它访问的是变量的当前绑定值,而不是变量在定义时指向的那个函数对象。
我们一步步走一遍代码执行流程:
- 第一次调用
t.dynamic_scoped_function()时,variable_function指向初始定义的、返回"not in nested function"的函数,所以输出对应内容。 - 调用
t.nested_function()时,内部执行了vairable_function = function() { return "in nested function"; }——这直接修改了外部作用域(test函数作用域)中variable_function变量的绑定值,把它指向了新的函数。 - 当
nested_caller调用dynamic_scoped_function时,dynamic_scoped_function依然访问外部作用域中那个已经被修改的variable_function变量,所以自然输出"in nested function"。
区分词法作用域和动态作用域
你提到的“依赖调用栈”是动态作用域的特点(比如Bash脚本就采用动态作用域),但JavaScript是词法作用域:函数的作用域由它定义时的位置决定,而非调用时的栈。那为什么你的代码看起来像动态作用域?因为你修改了外部作用域中变量的绑定——如果变量是不可变的(比如用const声明),就不会出现这种“不可预期”的结果:
// 修改后的示例:用const固定函数引用 (function() { var t, test; test = function() { const variable_function = function() { return "not in nested function"; }; // 用const声明固定引用 const dynamic_scoped_function = function() { return console.log(`variable function value: ${variable_function()}`); }; const nested_function = function() { // 这里如果再赋值variable_function会直接报错,因为const不可修改 const nested_caller = function() { console.log("calling dynamic scoped function from nested..."); return dynamic_scoped_function(); }; return { nested_caller }; }; return { dynamic_scoped_function, nested_function }; }; t = test(); t.dynamic_scoped_function(); t.nested_function().nested_caller(); }).call(this);
这段代码两次调用都会输出 "variable function value: not in nested function",因为variable_function的绑定被固定,不会被后续修改。
总结
你的代码表现是JavaScript的正常行为:
- JavaScript采用词法作用域,函数访问的是定义时所在作用域的变量。
- 如果变量是可变的(
var/let声明),后续对变量的重新赋值会影响所有访问该变量的函数调用结果。 - 这种“不可预知性”本质是可变变量带来的,而非作用域机制本身的问题——通过使用不可变变量(
const)或者用闭包捕获固定值,可以避免这种情况。
内容的提问来源于stack exchange,提问作者Cowsay
相关产品推荐
相关产品推荐

