闭包
function f1() {
var n = "Mozilla";
function f2() {
alert(n);}//f2可以访问f1中的n
return f2;//把f2作为返回值
}
var m = f1();
m();//就可以在f1外读取f1内部变量
描述
- 闭包可以让你从内部函数访问外部函数作用域。
- 子级可以向父级查找变量,逐级查找,直到找到,并且闭包能让函数的内部变量(局部变量)始终保存在内存中。
- 一个函数在定义时和它周围状态的引用捆绑在一起,函数可以记住并访问所在的词法作用域
- 内部函数的作用域链仍然保存对外部函数活动对象的引用
执行上下文
上下文有三种:
-
全局上下文
-
函数上下文==>arguments
函数执行上下文在函数调用时创建,若多次调用则都会创建一个新的上下文
执行上下文栈(调用栈,执行栈)储存代码执行期间创建的所有上下文,有LIFO(后进先出)特性
-
eval上下文
上下文中有三个属性:
- VO(变量对象),激活后为(AO)活动对象
- 作用域链
- this
for(var i=0;i<5;i++){
setTimeout(function(){
console.log(i++);
},4000);//5 6 7 8 9
}
console.log(i);//5
//setTimeout有执行队列
for(var i=0;i<5;i++){
(function(){
console.log(i++);
})();//0 2 4
}
console.log(i);//6
for(var i=0;i<5;i++){
(function(x){
setTimeout(function(){
console.log(x++);
},4000);
})(i);//0 1 2 3 4
}
console.log(i);//5
setTimeout是异步宏任务,不会直接执行,而会被放入任务队列里面,等待所有的同步代码执行完毕后才会执行。
为什么会一下输出56789,而不是每隔4秒输出,是因为在同步代码中通过for循环在极短的时间内就将5个异步任务放入了任务队列,他们几乎同时开始计时。所以隔了4秒后,所有的任务几乎同时完成。完成的任务会一个一个的从任务队列中取出,来到主线程中(也就是同步任务执行的线程)。
优点
方便调用上下文中声明的局部变量
可以在一个函数中再创建函数,避免传参问题
闭包的危害
内部函数被保留到外部,导致原有的作用域链不释放,造成内存泄露,
尤其在闭包中有DOM时和大量使用闭包时内存消耗很大
所以能用this就不用闭包