我们看一下官方对于闭包的解释,走起
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
自己的工作角度理解:闭包是一个函数,另外就是他是函数内的函数,他有需求,想要访问外部的作用域,那到底工作中怎么用呢?我用几个小例子给大家模拟一下
第一个小例子 我们想要打印一下一个变量调用值–情况
var a = 10;
function fn(){
a--;
console.log(a)
}
注释:有没有发现我在图片中打岔的地方,跟大家说一下原因,因为我声明的是全局变量,所以造成了在浏览器控制台任何人都可以控制我的变量,这个是老板不喜欢的,所以你懂得!!!
那么我们把全局变量放在函数中中不?
function fn(){
var a = 10;
a--;
console.log(a)
}
解释:这时候我们发现控制台不能被控制我的a变量,但是我们发现业务逻辑没有了,没有实现–情况,大家想一想,你的函数是不是一直都是调用的a=9,有人会说那不应该一直是8吗,因为流程是自上往下的,所以一直都是9,是一个自上往下的过程,大家要记清楚。
那我们再试试下边的情况
function fn(){
var a = 10;
return function fn1(){
a--;
console.log(a)
}
}
var niu = fn()
解释:我们会发现 业务需求达到了,老板的目的也达到了,在工作中我们经常用闭包。
总结 闭包要素有哪些???
1.是一个嵌套函数;
2.嵌套函数中内部函数要访问外部函数的变量;
3.内部函数作为一个返回值供使用
4.创建一个变量函数去接着这个闭包,让其使用
这时候我们明白闭包可以防止全局变量环境污染,但是单独的局部变量不能长期驻足,所以我们发现第二种情况函数用完销毁,重新用重新赋值,那么我们闭包这种机制就完美解决这个问题,让我们长期保留(不会在调用结束后,被垃圾回收机制回收)也能够不造成全局变量的环境污染。
世界上没有完美的事情,因为闭包会占更多的内存,因为她属于Function,会消耗更多的内存空间。
下边我再说一下工作中我们在哪些常见的场景使用闭包?
第一个 setTimeout()
function func(param){
return function(){
alert(param)
}
}
let fn = func('1');
setTimeout(fn,1000)
第二个 回调
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<link rel="stylesheet" href="">
</head>
<style>
body{
font-size: 12px;
}
h1{
font-size: 1.5rem;
}
h2{
font-size: 1.2rem;
}
</style>
<body>
<p>哈哈哈哈哈哈</p>
<h1>hhhhhhhhh</h1>
<h2>qqqqqqqqq</h2>
<a href="#" id="red">red</a>
<a href="#" id="blue">blue</a>
<a href="#" id="yellow">yellow</a>
<script>
function changeSize(color){
return function(){
document.body.style.color = color;
};
}
var red = changeSize('red');
var blue = changeSize('blue');
var yellow = changeSize('yellow');
document.getElementById('red').onclick = red;
document.getElementById('blue').onclick = blue;
document.getElementById('yellow').onclick = yellow;
//我们定义行为,然后把它关联到某个用户事件上(点击或者按键)。我们的代码通常会作为一个回调(事件触发时调用的函数)绑定到事件上
</script>
</body>
</html>
第三个封装一个私有变量
//共享的环境创建在一个匿名函数体内,立即执行。
//环境中有一个局部变量一个局部函数,通过匿名函数返回的对象的三个公共函数访问。
var a = (function(){
let b = 0;
function niu(val){
b += val;
};
return {
jiaYi:function(){
niu(1)
},
jianYi:function(){
niu(-1)
},
value:function(){
return b
}
}
})()
a.value();//0
a.jiaYi();
console.log(a.value())//1
a.jianYi();
console.log(a.value())//0
为节点遍历给事件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<link rel="stylesheet" href="">
</head>
<body>
<p id="info">123</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
<script>
function showContent(content){
document.getElementById('info').innerHTML = content;
};
function setContent(){
var infoArr = [
{'id':'email','content':'your email address'},
{'id':'name','content':'your name'},
{'id':'age','content':'your age'}
];
for (var i = 0; i < infoArr.length; i++) {
var item = infoArr[i];
document.getElementById(item.id).onfocus = function(){
showContent(item.content)
}
}
}
setContent()
</script>
</body>
</html>
上述代码原本想实现,点击不同的框显示不同的信息,结果现在都只会显示最后一项“your age”
分析:
循环中创建了三个闭包,他们使用了相同的词法环境item,item.content是变化的变量
当onfocus执行时,item.content才确定,此时循环已经结束,三个闭包共享的item已经指向数组最后一项。
解决:
/**
* 解决方法1 通过函数工厂,则函数为每一个回调都创建一个新的词法环境
*/
function showContent(content){
document.getElementById('info').innerHTML = content;
};
function callBack(content){
return function(){
showContent(content)
}
};
function setContent(){
var infoArr = [
{'id':'email','content':'your email address'},
{'id':'name','content':'your name'},
{'id':'age','content':'your age'}
];
for (var i = 0; i < infoArr.length; i++) {
var item = infoArr[i];
document.getElementById(item.id).onfocus = callBack(item.content)
}
}
setContent()
/**
* 解决方法2 绑定事件放在立即执行函数中
*/
function showContent(content){
document.getElementById('info').innerHTML = content;
};
function setContent(){
var infoArr = [
{'id':'email','content':'your email address'},
{'id':'name','content':'your name'},
{'id':'age','content':'your age'}
];
for (var i = 0; i < infoArr.length; i++) {
(function(){
var item = infoArr[i];
document.getElementById(item.id).onfocus = function(){
showContent(item.content)
}
})()//放立即执行函数,立即绑定,用每次的值绑定到事件上,而不是循环结束的值
}
}
setContent()
/**
* 解决方案3 用ES6声明,避免声明提前,作用域只在当前块内
*/
function showContent(content){
document.getElementById('info').innerHTML = content;
};
function setContent(){
var infoArr = [
{'id':'email','content':'your email address'},
{'id':'name','content':'your name'},
{'id':'age','content':'your age'}
];
for (var i = 0; i < infoArr.length; i++) {
let item = infoArr[i]; //限制作用域只在当前块内
document.getElementById(item.id).onfocus = function(){
showContent(item.content)
}
}
}
setContent()
喜欢的老铁们,点个赞吧!!!