前言
本文章源自《JavaScript知识小册》专栏,感兴趣的话还请关注点赞收藏.
上一篇文章:《JavaScript一文让你搞清什么是原型和原型链》
下一篇文章:《JavaScript令人心烦的this》
作用域
JavaScript中作用域即表示一个变量的合法使用范围,也就是变量在什么范围内可以被使用,超出了这个范围,变量将不能被读取到,会抛出异常
全局作用域
全局作用域跟当前JavaScript的运行环境关联,在全局作用域中定义的变量可以在js文件中随意访问
let a = 100
console.log('a is ', a)
输出a is 100
函数作用域
函数作用域则限制了在函数中定义的变量,只能在该函数体内被访问到
function printB(){
let b = 200
console.log(`b is ${b}`)
}
printB() // 输出 b is 200
console.log(b) // 会报错Uncaught ReferenceError: b is not defined
块级作用域
块级作用域是ES6后的新增特性,比函数作用域更加细粒度
function printB(){
let b = 200
console.log(`b is ${b}`) // 输出 b is 200
if (true){
let c = 300
console.log(`c is ${c}`) // 输出 c is 300
}
console.log(c) // 报错,Uncaught ReferenceError: c is not defined
}
printB() // 执行方法
自由变量
自由变量即一个变量在当前作用域中没有定义,但是却要使用,那么就只好向上级作用域开始,一层层寻找,直到找到为止,如果一直到全局作用域都没找到,则会报错xxx is not defined
let a = 100 // 全局作用域
function printB() { // 函数作用域
let b = 200
if (true) { // 块级作用域
let c = 300
let result = a + b + c
console.log(result)
}
}
printB() // 输出600
像以上例子,let result = a + b + c,在当前块级作用域中仅有变量c,并没有变量a和变量b,那么会往上一级作用域进行查找,接着在函数作用域中找到了变量b,但是还是找不到变量a,那么就再往上一级查询,最后顺利查询到了变量a b c并累加得出结果。如果从当前函数作用域查询,直到查询到全局作用域还是没找到变量,那么就会抛出异常xxx is not defined。
这也解释了为什么前面的在函数作用域访问变量c会报错的原因,因为在当前函数作用域找不到变量c,只会再往上一级全局作用域进行查找,但变量c只存在于块级作用域中,所以最终会报错。
闭包
闭包算是作用域的一种特殊情况,跟上边的写得相对固定的作用域嵌套的代码不同,闭包特殊在函数作为参数被传递,同时函数也可以作为返回值被返回
函数作为返回值
function printA() {
let a = 100 // 2.查询到变量a
return function () {
console.log(`a is ${a}`) // 1.当前作用域不存在a,向上级作用域查询
}
}
let fun = printA()
let a = 200
fun() // 3.输出 a is 100
函数作为参数
function printA() {
console.log(`a is ${a}`) // 当前没有变量a,向上查询,这里的上级作用域则是全局作用域
}
let a = 200 // 全局作用域
function print(fn) {
let a = 100 // 函数作用域
fn() // 实际调用printA()
}
print(printA) // 输出 a is 200
这里需要注意的是向上查询的作用域在编写代码时就已经决定了,不会因为函数在哪里被调用的从而影响到向上查询的作用域发生变化。记住这个原则就能搞明白为什么a is 200。
let a = 100; fn();那里的变量a只是混淆视听罢了。