闭包的本质就是闭包就是能够读取其他函数内部变量的函数。
闭包三个特点:
-
函数嵌套函数
-
函数内部可以引用函数外部的参数和变量
-
参数和变量不会被垃圾回收机制回收.
(1)函数作为返回值
window.οnlοad=function(){ function a () { var name = 'dav'; return function () { return name; } } var b = a (); console.log(b()); //注意b和b()的区别,在函数a中匿名函 //数函数作为一个变量返回给了a 但是匿名 //函数还是一个函数,console.log(b)打印 //的是一个函数,console.log(b())打印的 //是函数返回的值 }
在这段代码中,a()中的返回值是一个匿名函数,这个函数在a()作用域内部,所以它可以获取a()作用域下变量name的值,将这个值作为返回值赋给全局作用域下的变量b,实现了在全局变量下获取到局部变量中的变量的值。这个时候垃圾回收机制就不会回收name了。
function fn() { var num = 3; return function () { var n = 0; console.log(++n); console.log(++num); } } var fn1 = fn(); fn1(); fn1();
一般情况下,在函数fn执行完后,就应该连同它里面的变量一同被销毁,但是在这个例子中,匿名函数作为fn的返回值被赋值给了fn1,这时候相当于fn1=function(){var n = 0 ... },并且匿名函数内部引用着fn里的变量num,所以变量num无法被销毁,而变量n是每次被调用时新创建的,所以每次fn1执行完后它就把属于自己的变量连同自己一起销毁,于是乎最后就剩下孤零零的num,于是这里就产生了内存消耗的问题。
for (i = 0; i < 5; i++) { setTimeout(function () { console.log(i+''); }, '100') }
按照预期它应该依次输出1 2 3 4 5,而结果它输出了五次5,这是为什么呢?原来由于js是单线程的,所以在执行for循环的时候定时器setTimeout被安排到任务队列中排队等待执行,而在等待过程中for循环就已经在执行,等到setTimeout可以执行的时候,for循环已经结束,i的值也已经编程5,所以打印出来五个5,那么我们为了实现预期结果应该怎么改这段代码呢?(ps:如果把for循环里面的var变成let,也能实现预期结果)
for (var i = 0; i < 5; i++) { (function (i) { setTimeout(function () { console.log(i+''); }, '100') }(i)) }
引入闭包来保存变量i,将setTimeout放入立即执行函数中,将for循环中的循环值i作为参数传递,100毫秒后同时打印出1 2 3 4 5
for (var i = 0; i < imgs.length; i++) { !function (i) { setTimeout(function () { console.log(i); },i*100) }(i) }
在这段代码中,相当于同时启动3个定时器,i*100是为4个定时器分别设置了不同的时间,同时启动,但是执行时间不同,每个定时器间隔都是100毫秒,实现了每隔100毫秒就执行一次打印的效果。
var num = 15; function fn1(x) { if (x > num) { console.log(x); } } void function (fn2) { var num = 100; fn2(30); }(fn1);
在这段代码中,函数fn1作为参数传入立即执行函数中,在执行到fn2(30)的时候,30作为参数传入fn1中,这时候if(x>num)中的num取的并不是立即执行函数中的num,而是取创建函数的作用域中的num这里函数创建的作用域是全局作用域下,所以num取的是全局作用域中的值15,即30>15,打印30。