JS闭包及常见应用场景
1. 闭包的理解
理解闭包的关键在于:外部函数调用之后其变量对象本应该被销毁,但闭包的存在使我们仍然可以访问外部函数的变量对象。
它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的所有局部变量组成。
2. 闭包概念
能够读取其他函数内部变量的函数。 或简单理解为定义在一个函数内部的函数,内部函数持有外部函数内变量的引用。
3. 闭包用途
a. 读取函数内部变量
b. 让变量值始终保持在内存中,不会在调用后被自动清除
c.方便调用上下文的局部变量,利于代码的封装
4. 如何产生一个闭包函数
创建闭包最常见方式,就是在一个函数内部创建另一个函数。
function outer() {
let name = "hello"; // 闭包创建时所能访问的局部变量
function sayHello() // 闭包函数
{
alert(name);
}
return sayHello; // 返回闭包函数
}
let myFunc = outer();
myFunc();
注意:outer在被muFunc引用后,内存得不到释放,这样的函数多了会造成内存溢出。
解决方法:手动释放
myFunc = null;
5. 闭包的注意事项(如何防止内存泄漏)
通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,在创建了一个闭包以后,这个函数的作用域就会一直保存到闭包不存在为止。(注意注意!!相关的解释都注释在代码里)
function makeAdder(x) {
return function(y)
{
return x + y;
};
}
//add5 和 add10 都是闭包
//它们共享相同的函数定义,但是保存了不同的词法环境
let add5 = makeAdder(5);//add5中,x为5
let add10 = makeAdder(10);//add10中,x为10
console.log(add5(2)); // 7
console.log(add10(2)); // 12
//通过null释放对闭包的引用
add5 = null;
add10 = null;
在javascript中,如果一个对象不再被引用,那么这个对象就会被垃圾回收机制回收;如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。
6. 闭包应用之回调
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>闭包应用之回调</title>
</head>
<style>
body{
font-size: 10px;
/*将文本字体大小设为10px*/
}
</style>
<body>
<h1>闭包的应用</h1>
<p>闭包</p>
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>
<script>
//定义行为,然后把它关联到某个用户事件上(点击或者按键)。
//代码通常会作为一个回调(事件触发时调用的函数)绑定到事件上
function changeSize(size){
function change(){
document.body.style.fontSize = size + 'px';
};
return change;
}
//通过拿到a标签的id来调用函数
document.getElementById('size-12').onclick = changeSize(12);
document.getElementById('size-14').onclick = changeSize(14);
document.getElementById('size-16').onclick = changeSize(16);
</script>
</body>
</html>
7. 闭包应用之setTimeout
无论是setTimeout还是setInterval,传递的第一个函数都不能带参数(解决方法如下)
setTimeout(function(param)
{
alert(param)
},1000)
//通过闭包实现传参效果
function func(a){
return function()
{
alert(a)
}
}
var f1 = func(1);
setTimeout(f1,1000);
顺带一提前端面试的经典题:(此处敲黑板~叩叩叩)
修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5
for (var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000 );
}
此处理想的输出结果应该为:1,2,3,4,5
实际输出结果:5,5,5,5,5
原因:setTimeout()执行的是一个异步操作,当所有代码都执行完毕后才会执行setTimeout(),所以当setTimeout()函数执行时,for循环早已执行完毕,此时i的值已经变成5,所以输出的结果就为:5,5,5,5,5。
这道题觉得理解上有困难的小盆友可以看看下面这篇文章
解决方法:利用闭包。(当然还有其它方法,此处只讲闭包方法)
for (var i=1; i<=5; i++) {
function timer(i){
return function()//返回一个闭包函数
{
console.log(i);
}
}
var f1=timer(i);
setTimeout(f1,1*1000);
}
此时输出结果就为:1,2,3,4,5
8. 闭包应用之函数内部定时器
当函数内部的定时器引用了外部函数的变量对象时,该变量对象不会被销毁。
function() {
let a = 0;
setInterval(function(){
console.log(a++);
}, 1000)
}
如果是之前没有了解过闭包概念的同学,希望这篇文章能给你们一点点帮助啦,偶也是花了点时间(其实不止一点…)研究了些(海量)资料才搞明白的(其实还是太菜了)。如果本文中有错误的欢迎大神指出~
本文涉及到的相关参考资料:
https://www.jianshu.com/p/aa2da2bee95f(简书)