首先,先来说一下闭包的定义。什么是闭包,闭包就是有权访问另一个函数作用域内变量的函数,如例:
function abc(){
//这里是abc()作用域
let a = 1;
function add(){
//这里是add()作用域
a++;
console.log(a);//在这里可以访问到a变量
};
add();
add();
add();
}
console.log(a) //在这里会直接报错,无法访问abc函数中的a变量
abc();//输出2,3,4
要知道,js的每个函数都是一个个独立的密闭空间,它可以获取外界信息,但是外界却无法直接看到里面的内容。将变量 a 放进小黑屋里,只有add 函数能接触到变量 a,而且在函数 abc 外定义同名的变量 a也是互不影响的,这就是所谓的增强“封装性”。
看到了吗,这就是闭包,如此简单。
好,接下来,我们来看看她是怎么执行的
function abc(){
//这里是abc()作用域
let a = 1;
function add(){
//这里是add()作用域
a++;
console.log(a);//在这里可以访问到a变量
};
return add;
}
var c = abc();//这是函数表达式
c();//打印2
那我们可以看到,这里的c()就相当于是abc();,好,那有人会问我不是在var c = abc();的时候就已经把abc函数赋值给c了吗,难道c不等于abc()?,为什么下面一行我不可以直接调用c,还要给他加括号呢?
是这样的,在把abc函数赋值给c的时候,我真正赋值给他的是什么?在abc函数中,大家可以看一下,我返回了什么,我返回的是add,也就是说var c = abc();这一行相当于是var c = add;在这一步我的操作的是什
么,是不是进行了一次赋值,而我拿到的是一个标识符,一个函数名称,它并没有实际意义,在下一行在给了它()之后,它才具备了调用这个函数的资格。再看第二个例子:
function abc(){
//这里是abc()作用域
let a = 1;
function add(){
//这里是add()作用域
a++;
console.log(a);//在这里可以访问到a变量
};
return add();//大家看下这里返回的是什么
}
A实例:
var c = abc();//这是函数表达式
c;//打印2
或者可以这么写:
B实例:
var c = abc;//这是函数表达式
c();//打印2
大家可以看一下这两个例子,不同的地方在于返回的值,和赋给变量的值,在第二个例子中,我返回的是add(),所以,变量c就是add();在A实例中我赋给c的函数名称和函数体,在下一行,我就直接调用c就可
以了,但在B实例中,我赋给c的是函数名称,而没有函数体,在下一行,我就必须加上,同时调用他,才能得到返回值。
结论:在函数中真正重要的角色是什么,是函数体,也就是这个();
闭包的优点:
1.使用闭包代替全局变量
2.函数外或在其他函数中访问某一函数内部的参数
3.在函数执行之前为要执行的函数提供具体参数
4.在函数执行之前为函数提供只有在函数执行或引用时才能知道的具体参数
5.为节点循环绑定click事件,在事件函数中使用当次循环的值或节点,而不是最后一次循环的值或节点
6.暂停执行
7.包装相关功能
闭包的缺点:
1.内存消耗
通常来说,函数的活动对象会随着执行期上下文一起销毁,但是,由于闭包引用另外一个函数的活动对象,因此这个活动对象无法被销毁,这意味着,闭包比一般的函数需要更多的内存消耗。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2.性能问题
使用闭包时,会涉及到跨作用域访问,每次访问都会导致性能损失。因此在脚本中,最好小心使用闭包,它同时会涉及到内存和速度问题。不过我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响。