JavaScript中的作用域

63 阅读1分钟

前言

本文章源自《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只是混淆视听罢了。