局部作用域分为函数作用域和块作用域。
前言
局部作用域分为函数作用域和块作用域。
一、函数作用域
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
eg:
function fn(){
let a = 100
return console.log(a)
}
fn() //运行结果:100
console.log(a) //运行结果:Uncaught ReferenceError: a is not defined
如上所示,变量a无法在函数外部使用。函数执行完毕后,函数内部的变量就会被回收。
拓展:如果在函数中直接给一个未使用关键字声明的变量赋值,那么这个变量为全局变量,而全局变量一般不会回收,所以我们及其不推荐使用该方法。
二、块作用域
在JavaScript中使用 { } 包起来的代码称为代码块,代码块内部声明的变量外部有可能无法访问。
为什么要说外部有可能无法访问呢?
解释:比如在ES6之前只有var可以来声明变量,然而用关键字var声明的变量不会产生块级作用域(但是有函数作用域),所以在块级作用域内使用var定义的变量就可以被外部访问到,在ES6新增了let和const,let和const声明的变量会产生块级作用域,所以我们通常使用let和const来声明变量,防止全局变量污染。
我们来看一道题:
for (var i = 0; i < 3; i++) {
setTimeout(function (){
console.log(i)
},1000*i)
}
那么这道题会输出什么结果呢?
答案是 3 3 3
为什么不是 0 1 2 呢,这道题涉及了异步、作用域、闭包等问题,我们这里暂时先从作用域上分析。
因为 setTimeout 的 console.log(i); 的 i 是 var 定义的,所以是函数级的作用域,不属于 for 循环体,属于 global。等到 for 循环结束,i 已经等于 3 了,这个时候再执行 setTimeout 的三个回调函数(参考js的执行机制https://blog.csdn.net/qq_43141726/article/details/116378073),里面的 console.log(i); 的 i 去向上找作用域,只能找到 global下 的 i,即 3。所以输出都是 3。
解决方法:
这里的解决方法有多种,我们这里给出一种从作用域方向解决的方法:
for (let i = 0; i < 3; i++) { //把var改成let
setTimeout(function (){
console.log(i)
},1000*i)
}
let 为代码块的作用域,所以每一次 for 循环,console.log(i); 都引用到 for 代码块作用域下的i,因为这样被引用,所以 for 循环结束后,这些作用域在 setTimeout 未执行前都不会被释放。
总结
函数内部声明的变量,在函数外部无法被访问,let 和const声明的变量会产生块作用域,var 不会产生块作用域。