猜猜下面代码输出什么?
for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i,'b'); }, 1000); console.log(i,'c') } console.log(i,'a');
我猜不到啊。。。然后就从一个大神那儿学习了。
答案是:
分析:
由于setTimeout()实现异步的机制,代码console.log(i,'b');被指定到1s后执行,程序会先执行完console.log(i,'c');和console.log(i,'a');等他们执行完后再执行console.log(i,'b');
一、首先,要知道setTimeout()的运行机制:
setTimeout运行机制
setTimeout()和setInterval()的运行机制是,将指定的代码移出本次执行,等到下一轮Event Loop时,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就等到再下一轮Event Loop时重新判断。这意味着,setTimeout()指定的代码,必须等到本次执行的所有代码都执行完,才会执行。
Event Loop
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环);
举栗子:
test1(); setTimeout(otherTest,1000); test2();
其中test1和test2为立即执行任务,otherTest被setTimeout()定时器指定在1s后执行,此时otherTest被添加到任务队列的尾部,要等当前的脚本中Event Loop的任务队列全部执行完成后,才开始执行otherTest,但如果test1和test2耗时很长,前面的任务超过1s还未结束,此时otherTest只能等test1和test2运行结束,才会执行otherTest。
console.log(1); setTimeout(function(){console.log(2);},1000);//如果将setTimeout()的第二个参数设为0,就表示当前代码执行完(执行栈清空)以后,立即执行(0毫秒间隔)指定的回调函数。 console.log(3);
上面代码的执行结果是1,3,2,因为setTimeout()将第二行推迟到1000毫秒之后执行。
setTimeout(function(){console.log(1);}, 0); console.log(2);
上面代码的执行结果总是2,1,因为只有在执行完第二行以后,系统才会去执行"任务队列"中的回调函数。
二、什么是闭包?
内部函数访问外部变量,内部函数被外部函数调用。通俗的讲就是函数a的内部函数b,被函数a外部的一个变量引用的时候,就创建了一个闭包。
函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。
栗子:
function f1(){ var n=999; } alert(n); // error
将最开始的例子代码改为:
for (var i = 0; i < 5; i++) { (function(j) { // j = i setTimeout(function() { console.log(j); }, 1000); })(i); } console.log( i);
输出的结果为:5 -> 0,1,2,3,4 (使用了立即函数声明)
闭包的特性:
①.封闭性:外界无法访问闭包内部的数据,如果在闭包内声明变量,外界是无法访问的,除非闭包主动向外界提供访问接口;
②.持久性:一般的函数,调用完毕之后,系统自动注销函数,而对于闭包来说,在外部函数被调用之后,闭包结构依然保存在
系统中,闭包中的数据依然存在,从而实现对数据的持久使用。
优点:
① 减少全局变量。
② 减少传递函数的参数量
③ 封装;
缺点:
使用闭包会占有内存资源,过多的使用闭包会导致内存溢出等.