深入理解js作用域与作用域链

发布于:2024-05-07 ⋅ 阅读:(26) ⋅ 点赞:(0)

作用域

作用域规定了变量的可访问性和生命周期,帮助我们组织和管理代码。在 JavaScript 中,作用域可以分为全局作用域、局部作用域和块级作用域。

变量声明关键词

了解作用域之前,我们先了解一下变量的声明。在 JavaScript 中,有多种声明变量的方式:

  • 使用 var 关键字声明的变量存在变量提升,并且具有函数作用域或全局作用域。在浏览器中var声明的变量会成为全局对象window的属性。
  • 使用 let 和 const 关键字声明的变量存在块级作用域,并且不会被提升。
  • let 声明的变量可以被重新赋值,而 const 声明的变量是常量,不可被重新赋值。
  • const声明的同时需要赋值,而let,var可以不赋值。
关键词 作用域 声明时必须赋值 变量提升 重新赋值 重复声明
var 全局作用域/函数作用域
let 块级作用域
const 块级作用域

var声明的变量会成为全局对象window的属性。 image.png

var声明的变量存在变量提升,编译时会提升到全局作用域定顶端(或函数作用域顶端),而letconst不可以 image.png

var可重复声明

image.png

下面来看一些简单的例题

eg1:

const funList = [];
for (var i = 0; i< 10; i++) {
    funList[i] = function() {
        console.log(i);
    } 
}
console.log(i);
funList[0]();
funList[9]();

首先,我们知道var只有全局作用域和函数作用域,显然这里展示的是全局作用域,所以var i会被提到作用域顶端,当执行完for循环之后,此时i=10console.log(i)、funList[0]()、funList[9]()均输出10。

eg2:

const funList = [];
for (let i = 0; i< 10; i++) {
    funList[i] = function() {
        console.log(i);
    } 
}
funList[0]();
funList[9]();
console.log(i);

let块级作用域,所以变量i,只在for循环内起作用,因此执行到console.log(i)时(访问全局作用域),会报错"i is not defined",funList里中的函数各自生成了一块自己的作用域,因此它们分别输出了循环时i的值。

eg3:

for (var i = 0; i< 10; i++) {
	setTimeout(() => {
		console.log(i)
	}, 0)
}

for (let j = 0; j< 10; j++) {
	setTimeout(() => {
		console.log(j)
	}, 0)
}

因为var声明的是全局变量,而setTimeout是异步的,当主线程for执行完时i=10,然后再执行setTimeout,这是访问的是全局变量i,所以打印出来的都是10。而第二个for使用let声明的,它会形成各自的块级作用域,所以当执行setTimeout时,它们打印出来的是各自的j的值,也就是0~9

eg4:

const globalVal = 'globalVal';
function test() {
	var funVar = 'funVar';
	console.log(globalVal) // globalVal
	console.log(funVar) // funVar
}
test()
console.log(funVar)

这里的funVar属于函数作用域,所以在外部是无法访问funVar变量的,因此console.log(funVar)会报错"funVar is not defined"

全局作用域(Global Scope)

全局作用域是整个程序范围内可访问的作用域。在全局作用域中声明的变量可以被代码中的任何地方访问到。全局作用域的变量在程序执行过程中始终存在。

局部作用域(Global Scope)

局部作用域是在函数内部声明的作用域,只能在该函数内部访问。当函数执行结束时,局部作用域中的变量会被销毁,不再可访问。这种作用域可以帮助我们避免变量名冲突,提高代码的可维护性。

块级作用域(Block Scope)

ES6 引入了 let 和 const 关键字,使得 JavaScript 具备了块级作用域的能力。块级作用域由一对花括号 {} 创建,例如 if 语句、for 循环、withtry/catch等。在块级作用域中声明的变量只在该块内部可访问,超出该块则不可访问。

作用域链(Scope Chain)

JavaScript 中的作用域链是一种嵌套结构,它决定了变量的查找顺序。当在一个作用域内访问变量时,如果找不到该变量,JavaScript 引擎会逐级向外查找,直到找到匹配的变量或者到达全局作用域。

下面通过简单理解说明全局、局部、块级作用域以及作用域链

image.png

代码中,globalVar、globalLet、globalConst、i均为全局变量,在这一代码块中,均能被访问到,所以它们属于全局作用域。

模块1,属于全局作用域,模块2,属于块级作用域,因为第一个for是使用var声明的变量,所以不存在块级作用域,它属于全局作用域,因为var(只有全局作用域/函数作用域)存在变量提升。模块3属于局部作用域,声明的变量只在函数内起作用。当test()函数执行到console.log(globalVar)时,首先会在函数内选找globalVar变量,找不到,然后会一直向上级作用域寻找,它的上级作用域为模块1,所以在这过程,就形成了一条作用域链。