学习JavaScript中的作用域

215 阅读3分钟

「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战

什么是作用域(Scope)?

在维基百科是这样介绍的:

在电脑程序设计中,作用域(scope,或译作有效范围)是名字(name)与实体(entity)的绑定(binding)保持有效的那部分计算机程序。不同的编程语言可能有不同的作用域和名字解析。而同一语言内也可能存在多种作用域,随实体的类型变化而不同。作用域类别影响变量的绑定方式,根据语言使用静态作用域还是动态作用域变量的取值可能会有不同的结果。

简单讲作用域是指代码中定义变量的区域,包含了定义的变量查找规则。我们可以通过作用域可以找到相关变量。

下面举个例子:

var n = 0
console.dir(this)
console.log(n)

我们声明一个 变量 n 可以在当前作用域中找到我们定义的变量 n 。如果我们要使用变量 n 作用域会自动帮我找到相关变量。

静态作用域(static scope)

在维基百科是这样介绍的:

静态作用域又叫做词法作用域(lexical scope),采用词法作用域的变量叫词法变量。词法变量有一个在编译时静态确定的作用域。

我们现在用的 JavaScript 所使用的就是静态作用域。

我们可以通过简单列子来分析:

var n = 0

当JavaScript运行的时候首先会被分解为 varn0 。此时接下来进入语法分析阶段,varn0又叫词法单元会被转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树。这个树被称为“抽象语法树”(AST)。 最后是代码生成阶段,AST 会被转换为可执行代码。静态作用域是由写代码时将变量和所在位置所决定的。

全局作用域

JavaScript 中 在最顶部定义的变量就是全局作用域的变量。在浏览器环境中,JavaScript 默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性。

var n = 0

image.png

函数作用域

JavaScript 中 在某函数内部定义的变量就是函数作用域的变量

var n = 1
function fun(){
    var n1 = 2
}

image.png

块级作用域

ES6的时候新增了 letconst ,此时也引出了块级作用域。所谓的块级作用域简单点讲就是只在当前代码块里生效。

for(var i = 0;i<3;i++){
    console.log(i)
}
console.log("end",i) // 3

image.png

使用 ES6的 let 就不会出现上述情况了

for(let i = 0;i<3;i++){
    console.log(i)
}
console.log("end",i) // i is not defined

image.png

那么问题来了,在ES6之前如何解决上述问题呢?

我可以通过嵌一个自调函数来充当块级作用域来阻断变量 i 提升到全局作用域中。

(function(){
    for(var i = 0;i<3;i++){
        console.log(i)
    }
})()
console.log("end",i) // i is not defined

image.png

作用域链

当一个代码块出现嵌套时,就发生了作用域的嵌套。这我们在使用某个变量时。引擎就会依次往上找直到找到该变量或者已经找到全局作用域的时候就会停止。如果在

因此,当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止。如果在全局作用域中还未找到变量就会报错。

在当前作用域中找到此变量:

let n = 1
function fun(){
    let n = 2
    console.log(n) // 2
}
fun()

在全局作用域中找到此变量:

let n = 1
function fun(){
    console.log(n) // 1
}
fun()

在全局作用域中没有找到此变量:

function fun(){
    console.log(n) // n is not defined
}
fun()

1642501637(1).png