我理解的闭包就是在函数外面读取函数内部的变量,而且!使用闭包向外暴露的变量会一直存在与内存中,而不是被垃圾回收机制销毁。我认为闭包的重点是在后面这一句话。
普遍实现
先看看最普通的一个闭包函数
function f1(){
let a = 1
function f2(){
console.log(a)
}
return f2
}
let fn = f1();
fn();//1
以上就是一个最简单的闭包的实现,通过返回f2函数来访问到f1的内部变量a,不过这样似乎不能提现闭包的特点,下面再看看一段代码
function f1(){
let a = 1
function f2(){
console.log(a++)
}
return f2
}
let fn = f1();
fn();//1
fn();//2
fn();//3
连续调用fn之后发现,输出的a的值会被改变,这说明f1的局部变量a没有在调用f1之后被清除,而是一直保存在了内存中。原因在于f2被赋值给了一个全局变量,这导致f2一直存在于内存中,而f2依赖于f1,因此f1(的执行环境)也一直存在于内存中。
例子(坑)
**第一个坑:**for循环变量改变
let fn = []
for(var i = 0;i<10;i++){
fn[i] = function(){
console.log(i)
}
}
fn[0]();//10
fn[1]();//10
fn[2]();//10
这里的坑点就在于对于var来说,是不存在块级作用域的,所以最终调用的fn函数,每个的i都是去寻找了全局作用域的i,而for循环结束后,i就变成了10
for(var i = 0;i<10;i++){
setTimeout(()=>{console.log(i)},0)
}
//10
//10
//10....
这一段代码也一样,由于JS的eventloop机制,setTimeout会被当成宏任务最后再执行,于是执行的时候再去访问i的话也是访问的10,当改成let声明i的话,就会存在一个块级作用域,因此在setTimeout执行完之前,这个块级作用域是一直存在的,不会被立即清除
第二个坑:this指向
let name = "window"
let obj = {
name:"obj",
getName:function(){
return function(){
return this.name
}
}
}
obj.getName()();//window
这里的第二次调用其实是在window环境下调用的,this会指向window,而不是obj,再来看看另一种写法
let name = "window"
let obj = {
name:"obj",
getName:function(){
return this.name
}
}
obj.getName();//obj
这里的this就是指向的obj,因为是obj调用的它,再看看另一种写法
let name = "window"
let obj = {
name:"obj",
getName:function(){
let that = this
return function(){
return that.name
}
}
}
obj.getName()();//obj
这里用that保存了this的值,而getName是obj调用的,所以getName的this会指向obj,因此that也指向obj,所以输出了obj而不是window
到这里就是我对闭包的理解了