一、作用域
学习闭包之前必须理解JS当中变量的作用域。
变量的作用域无非就是两种:全局变量和局部变量。
- 函数内部可以直接读取全局变量
var n = 999;
function f1(){
// 函数作用域名
{
// 块级作用域
let a = 1;
}
console.log(n); // 999
}
f1();
- 函数外部自然无法读取函数内部的局部变量
function f2(){
const n = 'xww';
}
console.log(n); // undefined
二、闭包的形成
提到闭包先来聊聊闭包的作用:闭包可以帮助外部读取函数内部的变量,声明不带 var 全局变量。让我们来看下如何让局部变量可以在全局当中访问:闭包是指函数与其词法环境的组合。当一个内部函数被外部函数返回且引用了外部函数作用域的变量时,闭包就形成了。即使外部函数执行结束,内部函数仍能访问其变量,因为这些变量会被保存在内存中。借助闭包可实现数据私有和状态持久化。
三、如何介绍闭包
函数嵌套函数(作用域链的嵌套),f2在f1的里面形成了一个闭包,内部函数是需要返回的,且闭包也叫背包,自由变量不会被销毁。这完全得益于底层的垃圾回收机制,如果不做垃圾回收机制的话就会导致内存的泄漏。
// 让局部变量可以在全局访问
function f1(){
// 局部变量
var n = 999; // 自由变量
function f2(){
console.log(n);
}
return f2;
}
通常我们将内层函数叫闭包函数,闭包就是将函数内部和函数外部连接起来的桥梁。
四、闭包的用途
读取函数内部的变量 让这些变量的值始终保持在内存中
function f1(){
var n = 999;
nAdd = function(){
n += 1
}
function f2(){
console.log(n);
}
return f2;
}
var result = f1();
result(); // 999
nAdd(); // 全局的外部也可以访问
result(); // 1000
可以看到f1当中的函数nAdd没有用var声明,因此函数nAdd可以被外界访问,只要让内部变量可以跟外界沟通,打通了沟通的桥梁这就形成了闭包。因此在上述函数当中有两个闭包函数!!!(闭包可以帮助外部读取函数内部的变量)
var name = "The Window";
// let b = 2;
var object = {
name: "My Object",
getNameFunc: function () {
return this.name;
},
};
console.log(object.getNameFunc()); // MY Object
getNameFunc当中的this是指对象的方法进行调用的。
var name = "The Window";
// let b = 2;
var object = {
name: "My Object",
getNameFunc: function () {
return function () {
return this.name;
};
},
};
console.log(object.getNameFunc()()); // The Window
可以看到第二次的输出结果是The Window,是因为object.getNameFunc() 调用返回了一个匿名函数。紧接着 () 调用了这个匿名函数。在非严格模式下,独立函数调用时 this 会绑定到全局对象,在浏览器环境里全局对象就是 window 。(要使其的返回结果是My Object可以看到下面代码)
var name = "The Window";
// let b = 2;
var object = {
name: "My Object",
getNameFunc: function () {
// return this.name;
var that = this;
return function () {
return that.name;
};
},
};
console.log(object.getNameFunc()());
五、注意事项
我们可以观察看到多次执行闭包函数然后在垃圾回收机制当中看到
引用计数。闭包内存消耗大且可能引发内存泄漏,可在退出函数前销毁不用的局部变量。
此外,闭包能在父函数外部改变其内部变量值,存在不确定性,自由变量的生命周期也有影响。
六、总结
- 闭包是什么:闭包是一个函数加上到创建函数的作用域的连接,
闭包“关闭”了函数的自由变量。即变量a不会被垃圾回收啊!!!因为并不能确定里边的函数,也就是闭包这函数用不用这个a。
function fun(){
var a = 10
return function(){
console.log(a);
}
}
fun()()
- 闭包可以解决什么问题(闭包的优点) 2.1 内部函数可以访问到外部函数的局部变量
var lis = document.getElementsByTagName('li');
for(var i=0;i<lis.length;i++){
lis[i].onclick = function(){
alert(i);
}
}
// 先执行同步函数,最后再执行异步函数,所以一直打印为4
2.2闭包可以解决的问题
如何解决当点击每一个i出现0123的下标情况
var lis = document.getElementsByTagName('li')
for(var i = 0; i < lis.length; i++){
(function(i){
lis[i].onclick = function(){
alert(i);
}
})(i)
}
// 通过闭包的方式
- 闭包的缺点
3.1 变量会驻留在内存中,造成内存损耗问题。 解决:把闭包的函数设置为null
var lis = document.getElementsByTagName('li')
for(var i = 0; i < lis.length; i++){
(function(i){
lis[i].onclick = function(){
alert(i);
}
lis[i] = null;
})(i)
}
3.2 内存泄漏[ie] ==> 可说可不说,如果说一定要提到ie