闭包 是函数执行的一种机制
闭包机制:
我们函数执行产生的两大机制“保存/保护”,称之为闭包机制!!
【保护】:保护里面的私有变量不受外界的干扰. 防止全局变量的污染
【保存】:一但这个上下文形成不销毁的作用域 不被释放,里面的私有变量和值,就保存起来了,可以供其“下级”上下文中调取使用
1、闭包在实战中的应用
【1】闭包之私有变量的保护应用
【jquery】 通过window添加属性暴漏到全局
(function(){
function jquery(){
}
//把jquery 这个方法通过window添加属性暴漏到全局
window.jquery=window.$=jquery;
})()
在使用的时候: jquery() 或者$()
复制代码
【zepto】 把自执行函数的通过return把返回结果在外面用一个变量进行接收
var zepto=(function(){
return {
fn:function(){},
.....
}
})()
// 在使用的时候:zepto.fn
复制代码
【2】私有变量之保存机制-选项卡案例再忆、
【选项卡案例原版】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
ul,
ol {
list-style: none;
}
.main {
width: 500px;
margin: 0 auto;
}
ul>li {
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
border: 1px solid #333;
margin-right: 10px;
display: inline-block;
position: relative;
top: 1px;
}
.main>div {
height: 300px;
line-height: 300px;
border: 1px solid #333;
text-align: center;
display: none;
}
.main li.current {
background: darkcyan;
border-bottom-color: darkcyan;
}
.main div.current {
background: darkcyan;
display: block;
}
</style>
</head>
<body>
<div class="main" id="main">
<ul>
<li class="current">音乐</li>
<li>电视</li>
<li>综艺</li>
</ul>
<div class="current">音乐内容</div>
<div>电视内容</div>
<div>综艺内容</div>
</div>
</body>
</html>
<script>
var main = document.getElementById("main");
var lis = main.getElementsByTagName("li");
var divs = main.getElementsByTagName("div");
for (var i = 0; i < lis.length; i++) {
lis[i].index = i;
lis[i].onclick = function () {
var index = this.index;
change(index);
}
}
function change(index) {
for (var i = 0; i < lis.length; i++) {
lis[i].className = "";
divs[i].className = "";
}
lis[index].className = "current";
divs[index].className = "current";
}
</script>
复制代码
2、回忆当初里面的i 为啥是3
1、【作用域方式去思考】
当我们触发点击事件的时候,这个函数执行,形成私有上下文, 在这个私有上下文里面,并没有私有变量i,所以就会向上级上下文进行查找,此时上级作用文就是全局上下文里面的i,当我们发生点击事件的时候,此时for 循环早已完成,i早就是3
上下文:
- window 全局上下文 EC(G)
- 函数执行形成私有上下文
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
// 这里的i为啥会变成3?当我们触发点击事件的时候,这个函数执行,形成私有上下文,
// 在这个私有上下文里面,并没有私有变量i,所以就会向上级上下文进行查找,此时上级上下文就是全局
// 而全局作用上下里面的i,当我们发生点击时间的时候,此时for 循环早已完成,i早就是3
change(i);
}
}
复制代码
2、【同步异步事件去思考】
- 【同步事件】:当一件事件做完之后,再继续下一件事情
- 【异步事件】:当一件事件还没有做完,不再等待,直接去做下一个事件。所有的事件都是异步编程。
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
change(i);
}
}
复制代码
for循环是同步事件,执行完i=3;点击事件是异步事件,当我们点击页面上的按钮的时候,这个for循环早已经执行完了。
lis[0].onclick = function () {
change(i);
}
lis[1].onclick = function () {
change(i);
}
lis[2].onclick = function () {
change(i);
}
复制代码
解决方法:参照选项卡文档中选项卡解决方案
练习题
let x = 5;
const fn = function fn(x) {
return function (y) {
console.log(y + (++x));
}
};
let f = fn(6);
f(7);
fn(8)(9);
f(10);
console.log(x);
复制代码
浏览器的垃圾回收机制 GC 【“内存释放机制”】
1堆内存的释放机制
【谷歌】标记清除
- 如果当前堆内存的16进制地址,被其它事物所引用,则堆内存不能释放掉【强引用】
- 如果没有东西占用这个堆内存,浏览器在空闲的时候,会把这些未被引用的堆内存“回收/释放”掉
//此时不能被释放 因为被obj占用
let obj={
name:'zhufeng'
}
//obj=null;//让obj不指向对象的堆内存;这样上述对象就可以被释放掉了【手动释放堆内存的方法:赋值为null】
复制代码
2栈内存(执行上下文)释放机制
- 全局上下文是加载页面的时候产生的,也只有关闭页面的时候才会释放
- 刷新页面 会释放的全局上下文 重新产生新的全局上下文
- 私有上下文(无论是函数的私有上下文还是块级上下文)
- 一般情况下 执行完都会出栈释放掉,以此来优化栈内存
- 特殊情况:如果当前上下文中创建的某个对象(一般指一个对象{堆内存})被上下文以外的其它事物占用了,那么不仅创建的这个东西不能被释放和其有关联的这个私有上下文也不能被释放!!
- 这么做会消耗内存【慎用】
- 因为不被释放,所以这个私有上下文中的东西都被保留下来了,以后可以拿来用
思考
下面代码是否可以,每隔1000MS依次输出 0 1 2 3 4 5 ?
如果不可以,说明为啥?
以及如何解决?
// 不行:基于var在循环中声明的变量“i”是全局变量
// 第一轮循环 全局i=0 设置第一个定时器{1000} 给定时器设置的回调函数并没有执行,等待1秒后执行,循环继续
// 第二轮循环 全局i=1 设置第二个定时器{2000}
// ...
// 第五轮循环 全局i=4 设置第五个定时器{5000} i++,让全局的i=5,循环结束
// ->每一个定时器设置的回调函数,只有等到循环结束后,到了等待的时间,才会触发执行
for (var i = 0; i < 5; i++) {
setTimeout(function () {
/!*
* EC(AN)
*
* 作用域链:<EC(AN),EC(G)>
* 形参赋值:--
* 变量提升:--
*!/
console.log(i); //i不是自己私有的,是全局的,但是此时全局的i已经是循环结束后的5了
}, (i + 1) * 1000);
}
复制代码
解决方案如下
利用闭包机制解决 用了保存的应用
自执行函数
循环结束:设置了五个闭包,每一个闭包中都有一个自己的私有变量“i”,存储的值分别是0~4;全局i是5;
for (var i = 0; i < 5; i++) {
//手动设置个闭包
(function (i) {
setTimeout(function () {
console.log(i);
}, (i + 1) * 1000);
})(i)
}
复制代码
大函数返回小函数手动生成闭包
var fn=function(i){
return function () {
console.log(i)
}}
for (var i = 0; i < 5; i++) {
setTimeout(fn(i), (i + 1) * 1000);
}
复制代码
使用forEach
// new Array(5).fill(null) 创建一个长度为5的数组,每一项填充null,把其变为密集数组,这样可以forEach
// “_”就是形参占位,原本应该有个形参变量,但是我不想用,所以我占个位即可
new Array(5).fill(null).forEach(function (_, index) {
// 第一轮 EC(AN1) index=0 设置定时器{1000} 闭包
// 第二轮 EC(AN2) index=1 设置定时器{2000} 闭包
// ...
setTimeout(function () {
console.log(index);
}, (index + 1) * 1000);
});
复制代码
真实项目使用let
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, (i + 1) * 1000);
}
复制代码
终极方案:基于定时器传参「核心:闭包」
for (var i = 0; i < 5; i++) {
setTimeout(function (i) {
console.log(i);
}, 1000, i);
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END