JavaScript 闭包的使用场景

🌙
手机阅读
本文目录结构

TODO 闭包的特性问题??????

在什么场合下用

  • 1、保护变量;用自运行的匿名函数来实现;
  • 2、协调异步代码;自运行的匿名函数来实现,并且传参进去(异步代码需要用到原来的变量,通过作为函数的参数传进去;);

  实例

<!DOCTYPE html>
<html>
<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>
</head>
<body>
<div id="div1">
  <p>0000</p>
  <p>1111</p>
  <p>2222</p>
  <p>3333</p>
  <p>4444</p>
</div>
<script>
var oDiv = document.getElementById("div1");
var oPs = oDiv.getElementsByTagName("p");
for (i = 0; i < oPs.length; i++) {
  ;(function (i){
    oPs[i].onclick = function () {
      console.log(i);
    }
  })(i);
}
</script>
</body>
</html>

上面的也可以改造成这样的;[也是闭包的应用]

for (i = 0; i < oPs.length; i++) {
	oPs[i].onclick = function (i) {
		return function(){console.log(i);}
	}(i)
}

高程三上说一个函数里面返回一个函数,这样是才叫闭包;(个人认为闭包是一种机制;是一个封闭的作用域;只有形成封闭作用域都可以叫做闭包的);上面就是高程上说的标准闭包写法;

但是闭包的性能其实没有自定义属性好;可以用自定义属性来写;下面是用自定义属性的写法来做的;

for (i = 0; i < oPs.length; i++) {
	oPs[i].index=i;
	oPs[i].onclick = function () {
		console.log(this.index);
	}
}

关于作用域/闭包的其它理解;

var name = "china";
var age = 5000;
(function (name, age) {//形参变量也是私有作用域中的私有变量
  name = "zhu";
  age = 17;
  alert("zz");
  console.log(name, age);
})(name, age);//执行函数传递参数值,它只是把全局的name和age的值分别的赋值给我们的形参 ->"执行的时候传递的永远是一个值"
alert("22")
console.log(name, age);//先执行自执行函数,然后再执行此行

在函数里面修改全局的name和age(前提是私有的name和age依然存在)

var name = "china";
var age = 5000;
(function (name, age) {
    name = "zhu";
    age = 17;
    //把全局的也修改为"zhu"、7 ->在私有作用域中通过指定前缀来强行的修改的全局下的值
    window.name = name;
    window.age = age;
})(name, age);
console.log(name, age);//zhu ,17 

jQuery部分实现的原理:在私有中定义jQuery,通过window.xxx让其在全局下也可以使用

(function () {
    var jQuery = function () {
      console.log("核心代码");
    };
    window.jQuery = window.$ = jQuery;
})();
jQuery();
$();

闭包用在选项卡等效果上

	var banner = document.getElementById("banner");
	var tip = document.getElementById("tip");
	var oDivs = banner.getElementsByTagName("div");
	var oLis = tip.getElementsByTagName("li");
	//鼠标滑过某一个li,我们让所有的li和所有的div都没有选中的样式(select),然后让当前选中的这个li和对应的div有选中的样式
	
	function changeTab(nIndex) {
	  for (var i = 0; i < oLis.length; i++) {
	   oLis[i].className = null;
	   oDivs[i].className = null;
	  }
	  oLis[nIndex].className = "select";
	  oDivs[nIndex].className = "select";
	}

1、传统的循环绑定事件的方式

for (var i = 0; i < oLis.length; i++) {
    oLis[i].onmouseover = function () {
        changeTab(i);
    };
}

  > 这种方式不能实现我们的效果,为什么?   > 我们是循环所有的li,给每一个li都绑定了鼠标滑过的事件->

/*
i=0
oLis[0].onmouseover=function(){changeTab(i);}; 我们绑定事件其实是把匿名函数定义的主体赋值给元素的事件行为 -> oLis[0].onmouseover=xxxfff000;
i=1
oLis[1].onmouseover=function(){changeTab(i);};
...
i=4
oLis[4].onmouseover=function(){changeTab(i);};
i=5
循环结束
*/

当我们手动触发的这些行为的时候,比如触发第一个li的onmouseover,执行第一个绑定的方法:行程一个私有的作用域->形参赋值->预解释->代码执行,遇到了一个变量i,不是自己的私有的,找全局作用域下的i,这个i的值已经变为循环结束后的5,所以不是我们期望的索引值…

2、如何解决这个问题?

[作用域式解决]

当触发onmouseover的时候,全局的i变为了5,我们找全局的肯定不行,但是我可一这样来操作:

在当前私有作用域和全局作用域之间在嵌入一层作用域A,然后让A中有一个私有的变量i,存储的是自己的索引即可

for (var i = 0; i < oLis.length; i++) {
    (function (i) {
        oLis[i].onmouseover = function () {
            changeTab(i);
        };
    })(i);//每一次循环把当前i的值(0,1,2,3,4)赋值给形参i
}

函数执行形成不销毁的私有的作用域的第二种情况:

在一个函数中,给函数外面的DOM元素绑定事件 -> 由于给事件绑定的方法是一个引用数据类型值,而元素不属于这个作用域,而是函数外面的变量,这样也相当于外面的东西占用了函数里面的内存,当前的私有作用域不销毁

for (var i = 0; i < oLis.length; i++) {
    oLis[i].onmouseover = function (i) {
        return function () {
            changeTab(i);
        };
    }(i);
}

弊端:会形成多个不销毁的私有的作用域,占内存

[自定义属性的方式]

自定义属性处理问题是JS中最常用的编程思想

for (var i = 0; i < oLis.length; i++) {
	oLis[i].index = i;//每一次循环把当前li的索引存储到自己的自定义属性index上,后期需要用到索引的时候我们只需要把自定义属性index的值获取到拿来用即可
	oLis[i].onmouseover = function () {
		//this->当前鼠标滑过的这个li
		changeTab(this.index);
	};
}

案例2:用setTimeout实现把p相对上一个p向右移动100px;1秒中只实现一个P的移动;

setTimeout本身是同步的代码,但是setTimeout里面的函数是异步的代码;setTimeout告诉浏览器隔多少秒执行里面的函数;这个函数是脱离执行顺序的;属于异步代码; 代码如下:

var oDiv = document.getElementById("div1");
var oPs = oDiv.getElementsByTagName("p");
for (i = 0; i < oPs.length; i++) {
	(function(i){
		setTimeout(function(){
			oPs[i].style.left=100*i+"px";
		},i*1000)
	})(i);
}

因为定时器里面的函数是异步的,而setTimeout本身是同步的;所以i和里面的i不一致;用一个闭包把他们包起来;然后传i进去;或者直接包住setTimeout里面的函数;

setTimeout((function(i){
	return function(){oPs[i].style.left=100*i+"px";}
})(i),i*1000);

AXIHE / 精选资源

浏览全部教程

面试题

学习网站

前端培训
自己甄别

前端书籍

关于朱安邦

我叫 朱安邦,阿西河的站长,在杭州。

以前是一名平面设计师,后来开始接接触前端开发,主要研究前端技术中的JS方向。

业余时间我喜欢分享和交流自己的技术,欢迎大家关注我的 Bilibili

关注我: Github / 知乎

于2021年离开前端领域,目前重心放在研究区块链上面了

我叫朱安邦,阿西河的站长

目前在杭州从事区块链周边的开发工作,机械专业,以前从事平面设计工作。

2014年底脱产在老家自学6个月的前端技术,自学期间几乎从未出过家门,最终找到了满意的前端工作。更多>

于2021年离开前端领域,目前从事区块链方面工作了