在了解闭包之前我们可能还需要了解变量的作用域、IIFE立即执行函数表达式
目录
1.变量作用域
1.1 全局作用域
在函数外部定义的变量或函数,叫全局变量或全局函数。它们可以在当前程序的任意位置使用。在全局中定义变量可以用var,也可以直接添加window的属性。
生命周期:它们会一直占用内存,只能在当前文件中使用。如果想在多个文件中使用变量,需要用到cookie或本地存储。
1.2 局部作用域
在函数内部定义的变量或函数,叫局部变量或局部函数。它们只能在函数内部使用。在函数内部用var定义局部变量,如果用window定义的变量,则为全局变量。
生命周期:从调用时开始创建,到函数执行完后立即销毁。
1.3 块级作用域
在ES5中没有块级作用域,ES6中有。一对{}为一个块,块级作用域就是变量或函数只在当前这组{}中有效。在ES5中可以用IIFE实现块级作用域,ES6中用let实现。
2. IIFE立即执行函数表达式
2.1 什么是IIFE?
声明函数的同时立即执行调用这个函数。
定义的函数没有函数名,只能执行一次,无法在其它地方被调用,也不会造成全局污染。
2.2 IIFE的作用
可以用于解决运算中产生的全局污染问题(实际上它是一个闭包的写法)。
它也是ES5中实现块级作用域的方式。
2.3 IIFE和特点
1)将所有运算代码都放在匿名函数中;
2)函数是一个表达式,没有函数名,只能被执行一次;
3)执行一次后所产生的数据和变量就立即销毁,不会造成全局污染。
2.4 IIFE语法
(function(params){
。。。
})(params)
//将函数的定义和调用写在一起
2.5 IIFE的用法
(function(n) {
sum=0;
for (var i = 0; i<=n; i++) {
sum+=i;
}
window.yy=sum;
})(100)
console.log(yy);
var re=(function(n) {
sum=0;
for (var i = 0; i<=n; i++) {
sum+=i;
}
return sum;
})(10)
console.log(re);
3. 闭包
3.1 什么是闭包
闭包就是一种作用域的体现,函数外部可以访问函数内部 的数据。正常情况下,函数外部不能访问函数内部的变量,但是通过这种特殊的写法,将函数内的子函数暴露在全局上,可以在外部调用且可以访问到函数内部的数据,即可以让全局访问局部的数据
3.2 闭包的作用
- 实现函数外部可以访问函数内部数据的功能
- 减少了全局变量的使用,避免全局变量的污染
3.3 闭包的特点
- 闭包一定是函数内嵌套函数
- 闭包是一种作用域的体现,函数可以访问函数内的数据
- 闭包通常采用IIIFE写法,由于内部数据被全局所调用,将延缓资源的回收(即闭包中的变量的值用完后不会立即销毁,回驻留一段时间)。
3.3 闭包的缺点
- 闭包种的变量会占用更多的内存空间
- 可能会导致内存泄露
3.5 闭包的写法
(function(){
...
window.函数名=function(){
...
return 变量;
}
})()
(function(){
...
return function(){
...
return 变量;
}
})()
3.6 例子
需求:循环延时输出结果 0 1 2 3 4
每次for循环将i的值传给闭包函数,当for执行完后,执行回调时,这时i的每一个值仍驻留在内存中
for (var index = 0; index < 5; index++) {
(function (index) {
setTimeout(function () {
console.log(index);
},10);
})(index);
}
4. 内存管理(解决闭包引起的内存泄露)
在闭包中调用局部变量,会导致这个局部变量无法及时销毁,相当于全局变量一样会一直占用着内存。如果需要回收这些变量占用的内存,可以手动变量设置为null,然而在使用闭包的过程中,比较容易形成JavaScript对象和DOM对象的循环引用,就有可能造成内存泄露。这是因为浏览器的垃圾回收机制种,如果两个对象之间形成了循环引用,那么他们都无法被回收
(function () {
var tt=document.getElementById('tt');
tt.onclick=function(){
console.log(tt.innerHTML);
}
})();
在上面的例子中,出现函数间的嵌套形成了闭包,变量tt是JavaScript对象,引用了id为tt的DOM对象,DOM对象的onclick属性又引起了闭包,而闭包又可以调用tt,因此形成了循环引用,导致两个对象都无法被回收。。。
要解决此问题,只需要把循环引用中的变量设置为null即可
(function () {
var tt=document.getElementById('tt');
tt.onclick=function(){
console.log(this.innerHTML);
}
tt=null;
})();
如果在 func 函数中不使用匿名函数创建闭包,而是通过引用一个外部函数,也不会出现循环引用的问题。
(function () {
var tt=document.getElementById('tt');
tt.onclick=test;
})();
function test() {
console.log('node.js');
}