[JS基础回顾] 闭包 又双叒叕来~~~
闭包是基于词法作用域书写代码时所产生的自然结果,你甚至不需要为了利用它们而有意识地创建闭包
MDN的解释闭包是
函数
和声明该函数的词法环境
的组合。
Tips:
词法作用域
和词法环境
1,
此时函数还没被执行,所以使用的是词法作用域即静态作用域.2,
此时函数被执行,此时词法作用域
就会变成词法环境(包含静态作用域与动态作用域)
以上的解释 个人感觉还是不够清晰
我这样理解
- 闭包就是突破了函数作用域
- 闭包就是
函数嵌套函数
,子函数
可以访问父函数
的变量(也就是所谓的自由变量
), 所以,此变量不会被回收.
二 闭包暴露``函数作用域
的3种方式
:
1) 通过外部函数的参数
进行暴露
闭包内 调用外部函数 通过外部函数的参数 暴露 闭包内 自由变量.
function fn() {
var a = 2;
function innerFn() {
outerFn(a) //通过外部函数的参数进行暴露
}
innerFn();
};
function outerFn(val) {
console.log(val); // 2
}
fn(); // 2
2) 通过外部作用域的变量
进行暴露
其中val为全局变量
function fn() {
var a = 1;
function innerFn() {
val = a; //通过外部作用域的变量进行暴露
}
innerFn();
};
fn();
console.log(val); // 1
3) 通过return直接将整个函数
进行暴露
function fn() {
var a = 1;
function innerFn() {
console.log(a);
}
return innerFn; //通过return直接将整个函数进行暴露
};
let a = fn();
a(); // 1
三 关于闭包的内存泄露
首先必须声明一点:使用闭包并不一定会造成内存泄露,只有使用闭包不当才可能会造成内存泄露.
为什么闭包可能会造成内存泄露呢?原因就是上面提到的,因为它一般会暴露自身的作用域给外部使用.如果使用不当,就可能导致该内存一直被占用,无法被JS的垃圾回收机制回收.就造成了内存泄露.
注意: 即使闭包里面什么都没有,闭包仍然会隐式地引用它所在作用域里的所用变量. 正因为这个隐藏的特点,闭包经常会发生不易发现的内存泄漏问题.
常见哪些情况使用闭包会造成内存泄露:
使用定时器未及时清除
.因为计时器只有先停止才会被回收
.所以决办法很简单,将定时器及时清除
,并将造成内存的变量赋值为null(变成空指针)
相互循环引用
.这是经常容易犯的错误,并且也不容易发现.
- 将
闭包引用到全局变量
上.因为全局变量是只有当页面被关闭的时候才会被回收
.
四 循环和闭包
1) 同步循环打印 正确的值
for (var i=1; i<5; i++) {
console.log( i );
}
// 1 2 3 4
2) 同步中嵌套异步任务(中的宏任务)循环打印 错误的值
当执行 console 时, 循环已经完成, 同步任务执行完成后,执行宏任务,此时 i 已经是 5.所以打印5个5.
for (var i=1; i<5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
// 打印出 5 个 5
3) 创造5个独立的函数作用域,但是 i 也全都是对外部作用域的引用 错误的值
它的最终值仍然是5个5.为什么?我们来分析下,它用了一个匿名函数包裹了定时器,并立即执行.在进行for循环时,
会创造5个独立的函数作用域
(由匿名函数创建的,因为它是闭包函数).但是这5个独立的函数作用域里的i也全都是对外部作用域的引用
.即它们访问的都是i的最终值5.这并不是我们想要的,我们要的是5个独立的作用域
,并且每个作用域都保存一个"当时"
i的值
.
for (var i=1; i<5; i++) {
(function() {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
})();
}
// 打印出 5 个 5
4) 通过匿名函数创建独立的函数作用域,并且通过
变量 保存独立的 i 值
for (var i=1; i<5; i++) {
(function () {
var x=i;
console.log(x*1000); // 1000 2000 3000 4000
setTimeout( function timer() {
console.log( x );
}, x*1000 );
})();
}
// 1 2 3 4
5) 通过匿名函数创建独立的函数作用域,并且通过
参数 保存独立的 i 值
for (var i=1; i<5; i++) {
(function (x) {
console.log(x*1000); // 1000 2000 3000 4000
setTimeout( function timer() {
console.log( x );
}, x*1000 );
})(i);
}
// 1 2 3 4
注意
使用定时器未及时清除
.因为计时器只有先停止才会被回收
.所以决办法很简单,将定时器及时清除
,并将造成内存的变量赋值为null(变成空指针)
将闭包引用到全局变量
上.因为全局变量是只有当页面被关闭的时候才会被回收
.闭包就是函数嵌套函数
, 子函数
可以访问父函数
的变量(也就是所谓的自由变量
), 所以,此变量不会被回收.链接:https://juejin.cn/post/7011805931201642533