注册

[JS基础回顾] 闭包 又双叒叕来~~~

闭包是基于词法作用域书写代码时所产生的自然结果,你甚至不需要为了利用它们而有意识地创建闭包




MDN的解释闭包是函数声明该函数的词法环境的组合。




Tips: 词法作用域词法环境 1,此时函数还没被执行,所以使用的是词法作用域即静态作用域.2, 此时函数被执行,此时词法作用域就会变成词法环境(包含静态作用域与动态作用域)



以上的解释 个人感觉还是不够清晰
我这样理解

  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的垃圾回收机制回收.就造成了内存泄露.




注意: 即使闭包里面什么都没有,闭包仍然会隐式地引用它所在作用域里的所用变量. 正因为这个隐藏的特点,闭包经常会发生不易发现的内存泄漏问题.



常见哪些情况使用闭包会造成内存泄露:





    1. 使用定时器未及时清除.因为计时器只有先停止才会被回收.所以决办法很简单,将定时器及时清除,并将造成内存的变量赋值为null(变成空指针)




    1. 相互循环引用.这是经常容易犯的错误,并且也不容易发现.




    1. 闭包引用到全局变量上.因为全局变量是只有当页面被关闭的时候才会被回收.



四 循环和闭包


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

0 个评论

要回复文章请先登录注册