前言
闭包可以说是在JavaScript门前的“巨兽”,对于初学JavaScript的兄弟来说是很难理解的,我刚刚接触闭包也是理解不了,但是闭包对我们来说非常重要,在《你不知道的JavaScript》一书中甚至这样形容——对于那些有一点JavaScript使用经验,但从未真正理解闭包的概念的人来说,理解闭包可以看作某种意义上的重生。
如果你想真正的理解闭包,必须要知道这两兄弟——作用域与词法作用域,如果你对这两兄弟有深刻的认识,那你不会真正的理解闭包。
作用域
先看这样一句话“词法作用域是作用域的一种工作模式”,光从表面上看就可以知道,你如果想要理解词法作用域, 必须先要理解作用域的含义,没有作用域也就没有词法作用域;
什么是作用域
简单来说,作用域就是一个法则,用来确定在何处和如何查找变量(标识符)的法则,我们先来看看如何查找变量。
先来看一段代码:
function demo(){
var name = "LV";
console.log(name) //LV
}
demo();
在我们运行demo函数后,看到打印的结果为 "LV",我们已经把变量name输出了,但是变量name是从哪里来的呢,往上找我们发现,我们在前一行 声明了变量 var name = “LV”;
再看一段代码:
var city = "JiNan"
function demo2(){
console.log(city) // JiNan;
}
demo2();
这段代码输出了 city,在输出之前,首先在函数内部查找变量city,在函数内部没有找到,在向上层继续查找,在上层找到了,函数就停止查找,并对变量进行了输出。 这两段代码的区别就是:
- demo()是在函数内部找到了name变量对齐进行了输出。
- demo2()是在函数内部没找到,在全局函数找到的变量city,之后进行了输出。
也就是demo在函数作用域找到的变量,demo2是在全局作用域找到的变量; 查找变量的过程可以理解为,首先在自己所在的作用域中查找,如果找不到所需的变量继续向外层查找,如果在外层查找不到继续向外层查找,也就是链式查找 这条变量查找的链我们叫做作用域链。
作用域嵌套
在没有接触过ES6时,我们理解的作用域只有全局作用域和函数作用域,函数作用域在全区作用域之内,而函数作用域内部又可以 继续嵌套函数作用域。这就是作用域的嵌套。而我们查找变量的轨迹也就是作用域链 用代码来看嵌套作用域就是:
var a =1; //全局作用域
function fun1(){
var b =2; //函数作用域3
function fun2(){
var c =3 //函数作用域2
function fun3(){
var d =4; //函数作用域1
}
}
}
从里到外,这就是一条函数作用域链。
作用域中的变量查找规则
作用域中的变量查找规则 函数在执行 var name =“LV”时,其实是分为两个过程.
- 第一步 var name;
- 第二步 name = "LV";
这里有关变量提升和声明方式,详细可以看我前一篇文章
词法作用域
在上面我们已经介绍了作用域,起始作用域就是一套规则,函数查找变量的时候必须要遵循的一套规则,在开始时我们说过“词法作用于是作用域的一种工作模型” 作用域有两种工作模型,在JavaScript中词法作用域是主流的一种,还有一种应用较少的动态作用域。 所谓的词法作用域就是在写代码时将变量和块级作用域写在那里来决定的,在写代码的时候就已经确定了。 我们来看下面的一段函数:
function fn1(a) {
var b = a + 1;
function fn2(c){
console.log(a, b, c)
}
fn2(c * 3);
}
fn1(1); //1 2 6
- A 为全局作用域,有一个标识符:fn1
- B 为fn1所创建的作用域,有三个标识符:a、b、fn2
- C为fn2所创建的作用域,有一个标识符:c
- 作用域是由期代码写在哪里决定的,并且是逐级包含的。
在此强调,词法作用域就是作用域是由书写代码时函数声明的位置来决定的。编译阶段就能够知道全部标识符在哪里以及是如何声明的, 所以词法作用域是静态的作用域,也就是词法作用域能够预测在执行代码的过程中如何查找标识符。
下篇文章将进行闭包讲解的应用,如果这篇文章对你有所帮助,请点个赞支持下!感谢!若有不足,欢迎指正!