我来为你详细讲解“JavaScript进阶(二)词法作用域与作用域链实例分析”的完整攻略。
什么是词法作用域
词法作用域(Lexical Scope)是指变量在程序中的作用域是由它在代码中声明的位置所决定的。也就是说,变量的作用域在定义时就已经确定了,不会受到函数内部的影响。
词法作用域 vs 动态作用域
JavaScript 采用的是词法作用域,而不是动态作用域。动态作用域是指变量的作用域取决于它在运行时所处的上下文环境,而不是在定义时确定。这种作用域的语言包括 bash、Perl、Lua 等等。
我们看一个示例:
var x = 'global';
function test () {
console.log(x);
}
function foo () {
var x = 'function';
test();
}
foo(); // 输出: global
在这个例子中,test
函数里的 console.log(x)
输出的是全局变量 x
,而不是函数 foo
中定义的局部变量 x
,这就体现了词法作用域的特性。
作用域链
所谓作用域链(Scope Chain),就是在 JavaScript 的函数执行过程中,JavaScript 引擎会按照函数嵌套的层次查找变量的过程。
作用域链的查找顺序是由内而外、由下往上,最先查找当前函数的变量,如果找不到就往上一层函数中查找,直到找到全局作用域,如果还没有找到,则认为变量在当前作用域不存在。
我们来看一个作用域链的示例:
var x = 'global';
function outer () {
var y = 'outer';
function inner () {
var z = 'inner';
console.log(x, y, z);
}
inner();
}
outer();
在这个例子中,inner
函数中查找变量的过程是这样的:首先查找自身作用域中是否有 z
,有的话直接使用;如果没有,则查找父级作用域,也就是 outer
函数中是否有 y
,如果有,则使用;如果 outer
函数中也没有,则继续查找全局作用域中是否有 x
,如果有,则使用。
示例:
接下来,我们来看两个示例:
示例一
我们先来看一个不使用 var
声明变量的示例,这是一个非常典型的 JavaScript 陷阱:
function foo () {
x = 10;
}
foo();
console.log(x); // 输出: 10
在这个例子中,我们没有使用 var
声明变量 x
,而是直接在函数内对它进行赋值。此时,JavaScript 引擎会默认将其当作全局变量,并把它添加到全局对象(window)中。
因此,当我们在函数外部调用变量 x
的时候,会直接从全局作用域中查找,从而输出 10。
示例二
接下来我们看一个更复杂的示例:
var x = 'global';
function outer () {
var y = 'outer';
function inner () {
var z = 'inner';
console.log(x, y, z);
}
inner();
}
function foo () {
var x = 'function';
outer();
}
foo();
在这个例子中,我们定义了全局变量 x
,在函数 foo
中定义了局部变量 x
,然后调用了函数 outer
,outer
内部定义了变量 y
,inner
内部定义了变量 z
。
当我们调用 foo
函数后,控制台输出的结果是:global outer inner
。
这是因为在 inner
函数中,先查找自身作用域中是否有 z
,直接找到后输出;如果没有,则查找父级作用域,也就是 outer
函数中是否有 y
,找到后输出;如果 outer
函数中也没有,则继续查找全局作用域中是否有 x
,找到后输出。
注意,这里的 x
指的是全局变量 x
,而不是函数 foo
中定义的局部变量 x
,这就是词法作用域的特性。
总结一下:
词法作用域指变量在程序中的作用域是由它在代码中声明的位置所决定的,而不会受到函数内部的影响。
作用域链是在 JavaScript 的函数执行过程中,JavaScript 引擎会按照函数嵌套的层次查找变量的过程,并把查找的结果形成链式结构。
最后,我们需要注意在 JavaScript 中避免出现不使用 var
声明变量的情况,因为这么做会破坏作用域链,导致代码出现意料之外的结果。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript进阶(二)词法作用域与作用域链实例分析 - Python技术站