「前端每日一问(20)」说一下 JS 中的作用域、作用域链和词法作用域

329 阅读4分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

本题难度:⭐ ⭐

答:

作用域

在 JavaScript 中,我们可以将作用域定义为一套规则,这套规则用来管理 JS 引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找

用相对通俗一点的话来理解就是:

首先,编程语言都有变量的概念,没有变量,代码能实现的功能就非常有限,变量可以存储、读取、修改。

那么这些变量存储在哪里,怎么读取,总得有个管理变量的规则吧。

作用域就是编程语言设计出来的规则,用来管理变量。

JS 中常见的作用域主要分为几个类型:

  • 全局作用域 global/window
  • 函数作用域 function
  • 块状作用域 {}

全局作用域

既然叫全局作用域,从字面意思上理解,就是程序的任何一个地方都能拿到声明的变量。

任何不在函数中或是大括号 {}中声明的变量,都在全局作用域下。

var name = 'lin' // name 被定义在全局作用域下

function fn () {
  console.log(name) // 'lin'
}

fn()

console.log(window.name) // 'lin' 全局变量会挂载到 window 上

函数作用域

函数作用域也叫局部作用域,如果一个变量是在函数内部声明的它就在一个函数作用域下面。这些变量只能在函数内部访问,不能在函数以外去访问


function fn () {
  var personName = 'lin' // name 被定义在局部作用域下
  console.log(personName) // 'lin'
}

fn()

console.log(personName) // 从外部访问局部变量,报错,Uncaught ReferenceError: personName is not defined

块级作用域

es6 引入了 let 和 const 之后,在大括号中定义的变量会存在于块级作用域下,在大括号外面无法访问。

if (true) {
  var personName = 'lin' // name 被定义在块级作用域下
  const age = 18
  console.log(personName) // 'lin'
  console.log(age) // 18
}

console.log(personName) // 'lin' var 定义的变量,外部可以访问
console.log(age) // 报错,Uncaught ReferenceError: personName is not defined

作用域链

当在 JS 中使用一个变量的时候,首先 JS 引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域。

如果在全局作用域里仍然找不到该变量,它就会直接报错。

var sex = '男'
function foo () {
  var name = '阿林'
  function bar () {
    var age = 18
    console.log(name) // 阿林
    console.log(sex) // 男
  }
  bar()
  console.log(age) // Uncaught ReferenceError: age is not defined
}
foo()
  • bar 函数内部属于最内层作用域,找不到 name ,向上一层作用域 foo 函数内部找,找到了输出“阿林”
  • bar 内部输出 sex 时找不到,向上一层作用域 foo 函数找,还找不到继续向上一层找,即全局作用域,找到了输出“男”
  • foo 函数内部输出 age 时找不到,向上一层作用域找,即全局作用域,还是找不到则报错

词法作用域

词法作用域,也叫静态作用域,是指变量所处的作用域在代码编写时就已经确定了,而非执行阶段才确定的。

与之相对的是动态作用域,是指变量所处的作用域在代码执行阶段才确定。

JS 遵循的是词法作用域,下面这个例子可以证明:

var a = 2

function foo () {
  console.log(a) // 2  (不是 3!)
}

function bar () {
  var a = 3
  foo()
}

bar()

如果 JS 采用静态作用域,执行 foo 函数,先从 foo 函数内部查找是否有局部变量 a,如果没有,就去查找全局作用域,也就是 a 等于 2,所以结果会打印 2。

如果 JS 采用动态作用域,执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 a。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 a 变量,所以结果会打印 3。

输出的是 2,所以 JS 为词法作用域。

结尾

如果我的文章对你有帮助,你的👍就是对我的最大支持^_^

我是阿林,输出洞见技术,再会!

上一篇:

「前端每日一问(19)」JS 中函数为什么被称为一等公民?

下一篇:

「前端每日一问(21)」说一下 var、let 和 const 的区别