JavaScript | 详解变量作用域

在这里插入图片描述

作用域,可以理解为一个 “变量” 与其 “被赋予的值” 所’成立’的范围。

在 JS 中,当变量在执行环境中被声明的那一刻起,它就开始存在了。 但它与它所绑定的值可以影响的区域到哪里呢? 关于这个问题,就必须讨论到作用域。

作用域分类

  1. 全局级作用域(Global Level Scope)
  2. 本地级作用域(Local Scope)
  3. 块级作用域(ES6)
  4. 静态作用域(Static Scope)

全局级作用域

JavaScript 代码在编译阶段的最初,会产生一个全局执行环境 (Global Execution Context)。 而在全局的执行环境中,会存在一个全局的变量对象(Global Variable Object)。 只要是在全局环境中声明的变量 (或者函数),就会被存放在这个 Global Variable Object 内,并且可在整个程序的任何地方被访问。 这种变量,我们可以说它的作用域是全局 Global Level Scope,也就是所谓的全局变量 (Global Variable)。

因此,全局变量不管是在函数内,还是函数外被使用,都会起作用。

const number = 100  // 全局变量
const func = () => {
    
    
    console.log(number)  // 100
}
console.log(number)  // 100

像这个变量 number,在函数内外都可以被使用。

局部级作用域

一个变量如果是在函数内声明的。 它的影响就只限于这个函数最外层的 {} 中。 这种变量出了 {} 就完全不起作用了。 想要使用它会出现 ReferenceError。 而我们会把这种变量称之为局部变量。

const func = (() => {
    
    
    const number = 100  // 局部变量
    console.log(number)  // 100
})()
console.log(number)  // ReferenceError: a is not defined

另外,在函数外如果有着一个和函数内相同名称的全局变量,会优先使用函数内的区域变量。 如下:

const number = 999 // 全局变量
const func = (() => {
    
    
    const number = 100 // 局部变量
    console.log(number) // 100
})()
console.log(number) // 999

块级作用域

块级作用域 (Block Level Scope) 是一种更小的作用域。 只存在于 {} 中。 最常出现在 Function Scope 中的 {} 中(像是 if、for 等语法)。 聪明的你也许会想,是不是也可以将 Function Scope 看成是 Block Scope 的一种? 是的,虽然有点不同,但要这么想也不是不行。 更何况,在 ES6 之前,是没有块级作用域这种概念的。

在 ES6 之前,我们声明变量只能使用 var 来声明,而使用 var 声明变量会有不少缺点,也无法形成 Block Scope (只有 Function Level Scope 和 Global Level Scope)。 以至于 ES6 推出了另外两个声明变量的方式:const 和 let。

在函数中用 const 或 let 声明变量,都会立即让这个变量在函数中拥有 Block Level Scope。

let func = (() => {
    
    
    let number = 999; {
    
    
        console.log(number); // 999
    }
    console.log(number); // 999
})()
console.log(number); // ReferenceError: number is not defined

上面这段代码,使用了 let 声明变量 number; 所以 number 在 {} 内拥有块级作用域。 number 只成立于声明它的的 {} 内,以及它的子 {} 内。

let func = (() => {
    
    
    let number = 999;
    let number = 9999;
    console.log(number) // SyntaxError: Identifier 'number' has already been declared
})()

用 let 或 const 在同一个执行环境中不能重复声明变量

用 var 关键词声明变量的缺点这边稍做简述,就是因为这些缺点,才使得ES6之后,声明变量还是建议只使用 const 或者 let。

  1. 允许重复声明
  2. 不支持块级作用域( Block Scope)
  3. 不支持常数 (Constant) 特性

静态作用域

废话不多说,先看下面的代码:

var thisIs = 'global';
let func1 = () => {
    
    
    console.log(thisIs)
}
let func2 = () => {
    
    
    var thisIs = 'local';
    func1();
}
func1() // 'global'
func2() // 'global'

惊不惊喜? 意不意外? func2() 打印出 global 很合理,但 func1 竟然也印出 global。这是为什么呢?

某程序语言在 func1 的地方会印出 global ,这种程式语言采用的就是静态作用域 (Static Scope),例如:c / java / javascript ; 反之,如果在 func1 的地方打印出 local,这种程序语言采用的就是动态作用域 (Dynamic Scope),例如:perl。

静态作用域跟动态作用域最大的区别就是,静态作用域函数内的变量是在这个函数声明时就已经设定好的,也就是early binding。 所以不管这个函数在哪被呼叫,它内部的变量取值早就决定了,并不会因为被调用而发生改变。

而动态作用域,取决于函数被执行时代码的状态,进而决定函内的变量。 也就是所谓的late binding。

总之对于 Javascript,只要了解它采用的是静态作用域 (Static Scope) 就可以了。

补充:作用域链

还是先看代码:

let number = 1000000;
let outerFunc = () => {
    
    
    console.log(number)
  let innerFunc = () => {
    
    
        console.log(number)
    }
    innerFunc()
}
outerFunc()

虽然在函数内的执行环境中找不到 number 这个变量。 但内部的函数会一层层地往外部的执行环境中找去。 直到找到全局的执行环境为止。 如果还是找不到,就会抛出错误。 在 innerFunc 的执行环境内找不到 number 这个变量,就往 outerFunc 的执行环境找去; 一样找不到? 再往全局的执行环境找… 这样的行为 — 由内到外的这条找寻链,我们就称呼它为作用域链。

猜你喜欢

转载自blog.csdn.net/alexwei2009/article/details/127501494