理解闭包的前戏——作用域与词法作用域

652 阅读3分钟

前言

当每一个程序员小白刚深入了解JavaScript,就会遇到被誉为非常重要但难以掌握的知识——闭包 不过在开始学习闭包前,就不得不提到作用域与词法作用域,这两个基础知识是了解闭包的前置条件,所以我们先来了解作用域和词法作用域。

作用域

我们首先要了解,词法作用域是作用域的一种规则体系,所以没有作用域就没有词法作用域,我们由浅入深。

什么是作用域

作用域规定了在代码的哪个部分可以访问或查找特定的标识符。根据定义的宽泛程度和规则,作用域可以分为全局作用域、局部作用域、块级作用域等不同类别。

全局作用域,函数作用域

全局作用域和局部(函数)作用域在这篇文章有详细介绍 juejin.cn/post/737240…

块级作用域

块级作用域主要是使用let 与 const 来使用
我们演示一段

var arr=[];

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

}
arr.forEach(function(item){
    item()
})

这段代码在for循环下,会输出什么? 10个10 image.png 但是在我们的眼里,这段代码的输出应该是0~9,这就是全局作用域的效果 ,要是想让这段代码输出我们想要的结果 试试把for循环中的var变量改成let,我们再试试

var arr=[];

for(let i = 0; i <10; i++) {
        arr[i]=function(){
        console.log(i);
        }   
}
arr.forEach(function(item){
    item()
})

这就是用let关键字,将i变量绑定到for循环所在的快作用域内 image.png

而const则是用来创造块作用域变量,但值是定值,不像let可以是变量

var foo =ture;

if(foo){
    var a =2;
    const b =3;//包含在if中的块作用域常量

    a =3;//正常
    b=4;//错误
}

console.log(a);//3
console.log(b);//ReferenceError

作用域链

js引擎在查找变量时会先在函数中寻找,找不到就会根据outer的指向去到外层作用域中查找,层层往上,这种查找的关系链就称为 作用域链

function bar(){
    var myname ='Tom'
    let test1 = 100
    if(1){
        let myname = 'Jerry'
        console.log(test, myname);
    }
}
function foo(){
    var  myname = '彭于晏'
    let test = 2
    {
        let test = 3
        bar()
    }
}
var myname = 'john'
let test = 1
foo()

这里的输出值就是 1和Jerry

iwEdAqNwbmcDAQTRB4AF0QQ4BrDt9GD-hZtT8gY9Qd6owboAB9IZAt15CAAJomltCgAL0gAEANw.png_720x720q90.jpg

词法作用域

词法作用域由代码的物理布局或书写位置决定,即在编写代码时变量和函数的定义位置决定了其作用域。这意味着作用域在编译阶段就已经固定下来,不会在运行时改变。编译器或解释器在代码执行前的词法分析阶段,就能确定每个标识符的有效范围。
也就是说词法作用域是在变量声明的时候作用域就已经确定了,是静态的。 “编译器在当前作用域中声明一个变量name”,这个应该就是在执行上下文创建阶段生成VO变量对象时会有变量提升和函数提升的操作,var定义的变量会初始为undefined

其中eval()与with(){}方法可以欺骗词法作用域

function foo(a){
    eval(str)
    console.log(a,b);
}

foo(1,'var b = 2')

这段代码没有声明b,但在输出时,会输出b的值,eval可以直接使用str

var obj ={
    a:1,
    b:2,
    c:3
}
whit(obj){
    a = 3
    b = 4
    c = 5
}
console.log(obj);

这段代码输出时,a,b,c的值会变成3,4,5而不是2,3,4

结语

这就是作用域了,了解完这个概念,就可以开始尝试闭包了