我们时时刻刻在用,可以提炼成下面的总结:

  • 保护变量
  • 协调异步代码(除了上面2个外,我们还可以用其他场合的地方,我们先看例子;)
  • 实例

函数的这两种写法的区别;

(function(){})();//这个是定义和执行一起完成的;
function(){}//这个是定义一个函数;

JS代码是异步代码;并不是从上到下一句一句的执行;可以调用的时候执行;是异步的;是可以脱离其他代码,单独执行的;

<ul id="ul1">
<li>111</li>
<li>222</li>
<li>333</li>
</ul>
<script >
;(function(){
var ele=document.getElementById("ul1");
var oLi=ele.getElementsByTagName("li");

for(var i=0;i<oLi.length;i++){
oLi[i].onclick=function(){
alert(i);//这样的不行的;会弹出3;
//属于异步代码,因为onclick下的函数和浏览器读取代码的时候是不一样的;onclick绑定的函数无浏览器读取没有关系,也就是脱节了;这时候就是异步执行,不是同步执行的;这时候点击只会弹出浏览器原来的i只,也就是最后一次的i++(就是3);
//异步代码想获取原来作用域里的变量,这时候就需要用到闭包;
}
}

})()
var ele=null;
</script>

可以改下成下面这种,这样就是闭包的异步函数解决思路:

<ul id="ul1">
<li>111</li>
<li>222</li>
<li>333</li>
</ul>
<script >
;(function(){
var ele=document.getElementById("ul1");
var oLi=ele.getElementsByTagName("li");

for(var i=0;i<oLi.length;i++){
;(function(i) {
//上面这个i是形参,形参它是属于这个函数里面的;一个函数的形参,也是一个函数的变量;相当于一个通道/接口,把外面的值接收过来,并传到函数里面去;
oLi[i].onclick = function () {
alert(i);//这个时候就可以了,闭包就是解决异步时候的;
}
})(i);//这个里面的i是实参;和上面的形参i不是一码事;
}

})()
var ele=null;
</script>

上面写,是没有问题的;

  • oLi[i].onclick = function () {alert(i);

这个是会生成3个内存空间的地址,分布绑定给三个的onclick时间,这个时候是不会被销毁的;(自己的总结:闭包里面的东西如果储存到别的变量里,不会被销毁;(不知道总结的对不对)),

但是最好还是下面这样写,写成下面这种:下面才是典型的闭包函数;return方法容易理解;

<script >
;(function(){
var ele=document.getElementById("ul1");
var oLi=ele.getElementsByTagName("li");

for(var i=0;i<oLi.length;i++){
oLi[i].onclick=(function(n){
return function(){alert(n)}
})(i);
}
})()
var ele=null;
</script>

下面是一个小实例。打算让每个li,每隔一秒钟,移动一个。第一过1秒移动100px;第二个过2秒,移动200px;第三个过3秒移动300px;……

如果不间隔时间的,可以用下面的方法实现:

<style>
#ul1 li {
width: 100px;
height: 20px;
background: red;
position: relative;
}
</style>
<ul id="ul1">
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
<li>555</li>
<li>666</li>
</ul>
<script >
var oLis=document.getElementById("ul1").getElementsByTagName("li");
for(var i=0;i<oLis.length;i++){
oLis.item(i).style.left=(i+1)*100+"px";
}
</script>

用setInterval和setTimeout天生就是异步的;但是设置的时间,时间是同步的;只是里面的函数是异步的

setTime(function(){},1000)本身的执行与,setTime里面的a函数的执行不是一码事,他俩是没有关系的;a函数方法即是值,也是一个作用域;因为新开了一个作用域,这个作用域是异步的;

<script >
var oLis=document.getElementById("ul1").getElementsByTagName("li");
for(var i=0;i<oLis.length;i++){
window.setTimeout(function(){
oLis.item(i).style.left=(i+1)*100+"px";//item是返回元素的首个子节点;
},(i+1)*1000);//2、当i循环完以后,这个时候没有激活setTimeout的,因为它要过一秒钟后才执行,而此时循环已经结束了;
//1、执行循环i的时候,用的时间非常短,几乎可以忽略不计;
}
</script>

测试一个函数的运行时间:

var d=new Date;
for(var i=0;i<100000;i++){};//这个是循环100000次;
alert(new Date-d);//这个是循环一万次所用的时间。

刚才的定时器,中

oLis.item(i).style.left=(i+1)*100+"px";

其中的i是循环结束的5;而5已经超过数组的length了;这时候是把函数当做一个值来运行的了,而不是当做一个作用域来执行的;

所以是无效的;可以和上一个例子一样的思路来改写:

新建一个作用域;参数i会寻找传进来的for循环的i值来运行,这样就OK了;

<script >
var oLis=document.getElementById("ul1").getElementsByTagName("li");
for(var i=0;i<oLis.length;i++){
;(function (i) {
//上面的i是一个传参;
window.setTimeout(function() {
oLis.item(i).style.left = (i + 1) * 100 + "px";
}, (i + 1) * 1000);
})(i);//这个是for循环中的i
}
</script>

当然,还可以用return来做;

<script >
var oLis=document.getElementById("ul1").getElementsByTagName("li");
for(var i=0;i<oLis.length;i++){
window.setTimeout((function(i){
return function(){oLis.item(i).style.left=(i+1)*100+"px";}
})(i),(i+1)*1000)
}
</script>

上面这个是闭包的友好型;闭包既可以保护里面的代码不被别人污染了,也留了一个通道(就是参数)供别人来使用自己,同时然会自己内部的数值;【既可以保护自己,也可以和其他作用域互相搭配的使用,可以和其他作用域交流/交互】

这就是闭包的应用场合;

函数加不加()的区别;fn是函数的引用,fn()是函数的运行

fn的方法中,假设里面有return,但是外面不接收,说明这个函数是没有意义的了;就好比生产一堆面包,不吃也不卖。是没有意义的

window.setTimeout(fn,1000);//意思是1秒钟以后,执行fn; fn是指函数本身,这个时候把fn当做一个值来传给setTimeout这个方法的;setTimeout本身就是一个方法;也就是过一秒钟以后,才执行fn()这个方法;
window.setTimeout(fn(),1000);//这个时候也是可以的,关健是fn的返回值是什么类型的了,如果是一个值就没有意义了,如果返回的是一个函数,那么就是它返回的函数,在一秒钟执行;
//区别是,fn是指这个函数本身,fn()是指这个函数的返回值;

加()的,表示当时它就执行了,把返回的值,再返回给setTimeout;当成了setTimeout的一个参数了;也就是说,这个时候它的第一个参数不是fn,而是fn的返回值;如果是一个值,而不是一个方法,setTimeout会立即执行的;

<script >
function fn(i){alert("fn"+i);};
window.setTimeout(fn(9),1000);
alert(14);
//这个时候是fn(9)先执行;是直接把fn的值执行出来的;然后再开始做alert(14)
</script>
function fn(){alert("fn");};
window.setTimeout(fn(),1000);
alert(14);
//这个时候是fn()先执行(alert("fn"));是直接把fn的值执行出来的;然后再开始做alert(14)
window.setTimeout(fn,1000);
alert(14);
//这个时候谁先执行?
//答案:这个时候是alert先执行的;因为srtTimeout是一秒后才执行的;会先执行alert,执行完以后再回调setTimeout;
<script >
function fn(i){alert("fn"+i);};
window.setTimeout(function(){fn(9)},1000);
alert(14);
//这个时候是14先执行,执行完以后,再执行function(){fn(9)},因为fn(9)这种带()是返回的值的,我们需要给他外面再加一个套子。这样时候就可以既可以传参,也可以过1秒钟后执行了;
</script>

这个时候还可以把定时器时间设置为0;

<script >
function fn(i){alert("fn"+i);};
window.setTimeout(function(){fn(9)},0);
alert(14);
</script>

这个时候还是alert(14)先执行;即使定时器的时候是0;setTimeout是延时器,这时候是异步;异步不是一个队列的。所以是先执行alert(14);

<script >
function fn(i){alert("fn"+i);};
window.setTimeout(fn(9),0);
window.setTimeout(function(){fn(10)});
alert(14);//先弹出fn9,然后弹14,然后再弹fn10
</script>

注意:window.setTimeout(function(){fn(9)},0);和window.setTimeout(function(){fn(9)});是一样的,一个意思;

<script >
function fn(i){alert("fn"+i);};
function fn1(){return function(){alert("inner")}};
window.setTimeout(fn1(11),1000);
window.setTimeout(function(){fn(9)},1000);
window.setTimeout(function(){fn(10)});
alert(14);
//结果是:14、fn10、inner、fn9
</script>

函数如果没有参数,我们传进参数了,是可以的;(就好比即使你不贪财,我给你钱,这是不犯法的;)

闭包的经典题目:

<script >
f=function(){return true;};
g=function(){return false;};
(function(){
if(g()&&[]==![]){
//这个条件表示:!是单目运算符,优先级是最高的(像+-~!这些叫单目运算符),表示先把[]取非,就是false;就是[]==false;会全部转成数字来比;就是0=0对比;[]==![]就是true(拓展:对象和对象是永不相等的);这个时候的==优先级是高于&&的,g()是true(因为是闭包,它会先在闭包里找,找不到才去外面找;)因为刚才[]==![]就是true,这个时候是true&&true,所以这个是条件成立的;运行格式是g()&&([]==![]),先运行后面的比较,然后和g()对比;
f=function f(){return false;};
function g(){return true;}
}
})();
alert(f());//true?false?,
//这个alert(f())结果是false,因为条件成立的时候,里面的f把它赋值成了全局的变量,就是f变成false了(因为没有加里面的f前面没有加var,f是全局的变量了,如家了var代表的是局部的;那时候就是和外面的f没有关系的;);
alert(g());//这个时候还是false。(和那个闭包是没有一毛钱关系的,因为没有用变量来接收里面g()的返回值;)
//答案:false、false
</script>

如果把上面的g()条件换成f()。是如下解释;

<script >
f=function(){return true;};
g=function(){return false;};
(function(){
if(f()&&[]==![]){
f=function f(){return false;};
function g(){return true;}
}
})();
alert(f());
alert(g());//与这个闭包没有半毛钱关系;
//答案:如果上面闭包内的g()改成f();则答案是false,false;
//闭包内的f()在这里取的是上面的var f=function(){return true;};因为js里,如果方法是在等号右边的,浏览器是不做预解释的,因为没有预解释;f()在里面找不到f(),会到上一级去找,找到了f=function(){return true;};结果是true;[]==![]的结果是true。说明闭包的if条件还是成立的,所以闭包内的f还是会变成全局函数;alert(f());结果还是false,
</script>

 

二、闭包没有自闭症;

  • 保持个人—保护自己
  • 同时还能和它人友好沟通–留好接口(通道)—参数
  • 作用域在定义的时候就确定了;

 

 

······