这是我参与更文挑战的第 20 天,活动详情查看:更文挑战
JavaScript 这门语言使用的是词法作用域,即静态作用域,。这意味着函数执行时使用的是定义函数时的变量作用域,而不是调用函数时的变量作用域。与之相对的是动态作用域,即函数的作用域是在函数调用的时候才决定的。
来看一段代码,猜猜会输出什么?
let name = 'LvLin';
function funcA() {
console.log(name);
}
function funcB() {
let name = 'Lvalue';
funcA();
}
funcB() // 输出结果是什么?
复制代码
由于 JavaScript 采用的是静态作用域,所以funcA
输出的name
应该是在funcA
定义时所在的外层作用域,即全局变量name
,所以输出LvLin
。
如果是采用的动态作用域,那funcA
中的name
就是它被调用时所处作用域的name
,即funcB
中定义的,那输出结果就是Lvalue
了。
执行上下文
JavaScript 的执行上下文分为全局上下文和局部上下文,局部上下文包括函数上下文和块级上下文。变量或函数所处的上下文决定了它们可以访问哪些数据。
let name = 'LvLin'; // 处于全局上下文
function funcA() {
console.log(name); // 处于 funcA 的函数上下文
}
function funcB() {
let name = 'Lvalue'; // 处于 funcB 的函数上下文
funcA();
}
funcB()
复制代码
局部上下文在其所有代码都执行完毕后会被销毁,包括定义在上面的所有变量和函数。全局上下文在应用程序退出之后才会被销毁,比如退出浏览器。
执行上下文栈
每当代码执行流进入函数时,就会创建该函数的上下文并推入到一个上下文栈中,在该函数执行完后,上下文栈就会弹出该函数上下文,将控制权返还到上一个执行上下文。
通过 chrome
的开发工具,我们可以看到执行代码时的上下文栈的变化。
变量对象
每个执行上下文都有一个关联的变量对象,这个执行上下文中定义的所有变量和函数都存在这个对象上。
同样通过 chrome
的开发工具,我们能够看到每个上下文的变量对象。查看 funcB
的上下文变量对象如下图所示:
Local
显示当前函数中的变量,你还可以在源代码中看到它们的值高亮显示了出来。
Global
显示全局变量(不在任何函数中)。
作用域链
执行上下文的代码在执行的时候,会创建变量对象的一个作用域链。这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文的变量对象始终位于作用域链的最前端。
作用域链的下一个变量对象来自包含上下文,再下一个变量对象来自再下一个包含上下文,以此类推,全局上下文的变量对象始终是作用域链的最后一个变量对象。
代码执行时的标识符解析(即获取变量的值)是沿着作用域链去逐级搜索该标识符名称,直到找到该标识符,如果没有找到,通常会报错。
let name = 'LvLin';
function funcA() { // 作用域链为 全局变量对象 -> funcA 变量对象
console.log(name); // 当前变量对象没有 name,沿着作用域链到全局变量对象查找
// 找到 name = 'LvLin'
}
function funcB() {
let name = 'Lvalue';
funcA();
}
funcB()
复制代码
最后
如果文章对你有帮助,点个赞呗~