- 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);