3.JavaScript函数作用域
JavaScript函数作用域
介绍
这个系列是关于访问JavaScript基本概念和更重要的细节。在这节中,我们将学习另一个重要的概念,称为作用域。我们还将学习和欣赏作用域链的重要性。
测验时间
下面代码执行的输出会是什么,为什么?
function jerry() {
console.log(name);
}
function tom() {
var name = 'tom';
jerry();
}
var name = 'cartoon';
tom();
它会是cartoon
,tom
还是undefined
?但更重要的是,你是如何在这里决定答案的?你是按作用域
吗?执行上下文
呢?
作用域
我上面问的问题的答案是,cartoon
。让我们进一步探索和理解它。
在JavaScript中,作用域是确定变量存在位置以供使用的机制。变量可能存在于函数调用内部或外部。
让我们将上面的代码分解为几个部分,看看变量的可访问性如何根据变量的声明位置和函数的创建而变化。
回顾
以下是我们对JavaScript执行上下文的理解中的一些关键点:
- 有一种叫做全局执行上下文和函数执行上下文的东西
- 每个执行上下文都有一个特殊的东西,称为this和对
外部环境
的引用。 - 当我们调用一个函数时,JavaScript引擎会为当前函数执行上下文创建一个外部引用。
- 该函数可以访问外部引用中定义的变量。当JavaScript引擎在当前执行上下文中找不到它时,它会进行查找。
作用域和作用域链
在上面的示例中,有两个函数调用,tom()和jerry()。因此将创建两个不同的函数执行上下文。
请记住,总是有一个全局执行上下文,其中关键字this
等于Window
对象。因此,我们这里总共有三个执行上下文,一个全局执行上下文和两个函数执行上下文,分别是tom()
和jerry()
。
- 变量
name
是在全局执行上下文中创建的,并在执行阶段分配了一个cartoon
的值。
var name = 'cartoon';
- 调用函数
tom()
时,JavaScript引擎为tom()
创建了一个执行上下文和一个对外部环境的引用,即全局执行上下文
tom();
- 当
tom()
调用jerry()
时,JavaScript引擎识别jerry()
的词法
位置并执行相同的操作。它创建jerry()
的执行上下文和对外部环境的引用。
function tom() {
var name = 'tom';
jerry();
}
等等。jerry()
的外部环境是什么?是tom()
的执行上下文还是全局执行上下文?这取决于另一个问题的答案。
谁创造了
jerry()
?它在词汇上的位置?
jerry()
是由全局执行上下文创建的,即使它是在tom()
的执行上下文中调用的。我们发现jerry()
在词法上位于全局执行上下文并由它创建。根据这个理论,jerry()
有一个指向全局执行上下文的指针。
到目前为止,我们还发现,jerry()
中没有声明名为name
的变量。在执行阶段,它尝试记录name
变量。
function jerry() {
console.log(name);
}
在执行阶段,JavaScript引擎按照jerry()
的外部引用启动查找过程,并在全局执行上下文中找到一个用value为cartoon
创建的变量name
。
现在我们知道为什么这个问题的答案必须是cartoon
,而不是tom
或undefined
。这是范围界定如何发生的视觉流程
在当前执行上下文和外部引用中查找变量的整个过程形成了一个称为作用域链的链,我们还可以断定变量name
在函数jerry()
的作用域中,因为它是在它的作用域链中被成功找到的。
链的变化
此代码执行的输出将是什么?
function tom() {
var name = 'tom';
function jerry() {
console.log(name);
}
jerry();
}
var name = 'cartoon';
tom();
我们对上面的代码做了一点小改动。现在函数jerry()
是在tom()
中创建的。jerry()
的执行上下文对外部环境的引用将指向tom()
的执行上下文。因此,变量name
将在tom()
函数中定义的作用域链中找到。所以你知道答案是,tom
!
区块作用域
当我们了解作用域的基础知识时,让我们了解什么是块作用域。代码块由这些大括号{…}
定义。如果在代码块中使用名为let
的关键字声明变量,则它仅在该块中可见。
{
let name = "tom";
console.log(name);
}
console.log(name);
如果我们用var
而不是let
创建变量name
,我们就不会发现这个块作用域限制。这里有另一个例子,
{
let name= "tom"; console.log(name);
}
{
let name = "jerry"; console.log(name);
}
这将非常好地工作,并将tom和Jerry记录在控制台中
即使对于if
、for
、while
等,在块({…}
)内声明的变量也只能在其中可见。这是一个for
循环的例子,
for (let counter = 0; counter < 10; counter++) {
console.log(counter);
}
console.log(counter);
总结
使用执行上下文、外部引用、词法定位等基本概念来理解范围。,将有助于轻松调试棘手的错误(那些可怕的生产错误)。作为JavaScript开发人员,我们将对内部工作方式更加自信。
转载自:https://juejin.cn/post/7250113163332517946