J35 闭包

210 阅读2分钟

1.函数执行,形成一个私有的执行上下文EC(XX)

  • 保护作用:在这个上下文中会有一些私有的变量AO(XX),这些私有的变量和外界的变量不会冲突(互不影响)
  • 保存作用:某些情况下,上下文中的某些内容被外部占用后,当前上下文并不会出栈销毁,这样就会把上下文中的一些信息存储起来
2.利用闭包的保护作用
  • 1.团队协作开发中,A/B共同开发一个页面,最后要把代码合并在一起,为了防止全局变量的冲突污染,我们建议每个开发者,都把自己的代码放置到一个闭包中(自执行函数执行即可,这样就是私有的上下文)保护起来
  • 2.如果我们是封装一个插件或者类库等,为了防止我们定义的变量和方法 和 用户定义的冲突,我们也是需要把所有写的代码放到一个闭包中,例如:jQuery...
3.市面上理解的闭包
  • 其实,函数执行会形成一个私有上下文的机制就被称为“闭包”,只不过市面上认为,一般的上下文执行完就出栈销毁了,只有那些没有被销毁的上下文才能被称为“闭包”
4.闭包的写法()()
  • 1、自执行函数的写法:函数前面加() ~ ! - + ; - +不太合适;

  • 2.小组开发:各自形成一个闭包

    //A的代码: (function anonymous() { //=>自执行函数执行,会形成一个私有的上下文 //=>在这里声明+定义的变量或者函数都是私有的 var x = 100, y = 200; function func() { // ... } })();

    //B的代码: ~ function anonymous() { // console.log(anonymous); //=>函数本身匿名函数设置的函数名只能在函数里面应用 //=>函数外面是无法访问的 var x = 200, n = 0;

    function func() { // ... }

    function handled() { // ... } }(); console.log(anonymous); //=>(自执行函数,把函数用闭包保护起来,所以外面不能使用) //=>Uncaught ReferenceError:

5.选项卡

<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8">
	<title></title>
	<!-- IMPORT CSS -->
	<link rel="stylesheet" href="reset.min.css">
	<style>
		.tabBox {
			margin: 60px auto;
			width: 500px;
		}

		.tabBox .tab {
			position: relative;
			top:-36px;

		}

		.tabBox .tab li {
			float: left;
			margin-right: 10px;
			padding: 0 10px;
			height: 35px;
			line-height: 35px;
			font-size: 14px;
			border: 1px solid #AAA;
			background: #f6f7fb;
			cursor: pointer;

		}

		.tabBox .tab li.active {
			background: #FFF;
			border-bottom-color: #FFF;
		}

		.tabBox div {
			display: none;
			padding: 10px;
			height: 100px;
			border: 1px solid #AAA;
			background: #FFF;

		}

		.tabBox div.active {
			display: block;
		}
	</style>
</head>

<body>
	<section class="tabBox" id="tabBox">
		<ul class="tab clearfix">
			<li class="active">编程</li>
			<li>读书</li>
			<li>运动</li>
		</ul>
		<div class="active">编程可以使我“赚取高薪”</div>
		<div>读书可以使我“修身养性”</div>
		<div>运动可以使我“身体健康”</div>
	</section>

	<!-- IMPORT JS -->
<script>

var tabBox = document.getElementById('tabBox'),
	navList = tabBox.getElementsByTagName('li'),
	conList = tabBox.getElementsByTagName('div');

// changeTab:实现页卡切换的方法
function changeTab(index) {
	// index存储的是当前点击这一项的索引
	for (var i = 0; i < navList.length; i++) {
		navList[i].className = '';
		conList[i].className = '';
	}
	navList[index].className = 'active';
	conList[index].className = 'active';
}

//1、let实现:闭包的保存作用
// for (let i = 0; i < navList.length; i++) {
// 	// 利用ES6中块上下文(作用域)的概念,每一轮循环都会形成一个私有的上下文,里面记录了私有变量的i,i的值分别是每一轮循环的结果 0/1/2... 
// 	navList[i].onclick = function () {
// 		changeTab(i);
// 	};
// }	

//2、 自执行函数:利用闭包的保存作用:形成一个不被释放的私有上下文(这种方式是不好的,因为循环多少次,就形成多少个不销毁的上下文,很消耗性能,真实项目中是不推荐使用的)
// for (var i = 0; i < navList.length; i++) {
// navList[i].onclick = (function (i) {
// 	// i也是闭包中的私有变量
// 	return function () {
// 		changeTab(i);
// 	}
// })(i); // i是每一轮循环全局下的i
// }

/* 
i=0 第一轮循环
    navList[0].onclick = (function (n) {
		自执行函数执行,形成一个私有的上下文EC(AN1)  [此上下文不会销毁]
			作用域链:<EC(AN1),EC(G)>
			形参赋值:n=0
			变量提升:--
			代码执行:
		//=>return BBBFFF000;
		return function () {
			changeTab(n);
		} 
	})(i); //=>把当前全局变量i的值作为实参传递给自执行函数 ...(0)

	navList[0].onclick=BBBFFF000;

i=1 第二轮循环
	navList[1].onclick = (function (n) {
		自执行函数执行,形成一个私有的上下文EC(AN2)  [此上下文不会销毁]
			作用域链:<EC(AN2),EC(G)>
			形参赋值:n=1
			变量提升:--
			代码执行:
		//=>return BBBFFF111;
		return function () {
			//=>点击哪一个LI就会把它绑定的办法执行,也会形成私有上下文EC(LI2)
			// 作用域链:<EC(LI2),EC(AN2)>
			// 形参赋值:--
			// 变量提升:--
			// 代码执行:
			changeTab(n); //=>n不是私有变量,是EC(AN2)中的1
		} 
	})(1);

	navList[1].onclick=BBBFFF111;

......

循环结束后,全局i=3;这一套循环结束,我们每一轮循环都创建了一个不销毁的私有上下文(闭包):EC(AN1)~EC(AN3),而且每一个闭包中都有一个私有变量n,存储的是本轮循环全局i的值(EC(AN1)中的n=0  EC(AN1)中的n=1 ...)
 */

 //3、[].forEach.call  这种方案的原理就是闭包,和自执行函数的原理是一样的
//  [].forEach.call(navList, function (item, index) {
// 	item.onclick = function () {
// 		changeTab(index);
// 	}
// });

//4.循环绑定点击事件(基于自定义属性解决循环事件绑定中索引i的问题)
for (var i = 0; i < navList.length; i++) {
	navList[i].index = i;
	navList[i].onclick = function () {
		changeTab(this.index);
	};
}
</script>
</body>

</html>