理解闭包

理解闭包前的概念

了解闭包 首先必须理解变量作用域,js有全局作用域和函数作用域。函数内部可以直接读取全局变量。但是,函数外部无法读取函数内部声明的变量。

其次了解, JavaScript 语言特有的”链式作用域”结构(chain scope)解释:子对象会一级一级地向上寻找所有父对象的变量,父对象的所有变量,对子对象都是可见的,反之则不成立。

闭包定义

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数里创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破链式作用域,将函数内部的变量和方法传递到外部。不管函数走到哪里,定义时的作用域就带到了哪里。

1
2
3
4
5
6
7
8
9
10
11
//例题1
var inner;
function outer(){
var a=250;
inner=function(){
alert(a);//这个函数虽然在外面执行,但能够记忆住定义时的那个作用域,a是250
}
}
outer();
var a=300;
inner();//一个函数在执行的时候,找闭包里面的变量,不会理会当前作用域。
1
2
3
4
5
6
7
8
9
//例题2
function outer(x){
function inner(y){
console.log(x+y);
}
return inner;
}
var inn=outer(3);//数字3传入outer函数后,inner函数中x便会记住这个值
inn(5);//当inner函数再传入5的时候,只会对y赋值,所以最后弹出8

闭包的内存泄漏

栈内存提供一个执行环境,即作用域,包括全局作用域和私有作用域,那他们什么时候释放内存的?

  • 全局作用域—-只有当页面关闭的时候全局作用域才会销毁
  • 私有的作用域—-只有函数执行才会产生

一般情况下,函数执行会形成一个新的私有的作用域,当私有作用域中的代码执行完成后,我们当前作用域都会主动的进行释放和销毁。但当遇到函数执行返回了一个引用数据类型的值,并且在函数的外面被一个其他的东西给接收了,这种情况下一般形成的私有作用域都不会销毁。

如下面这种情况:

1
2
3
4
5
6
function fn(){
var num=100;
return function(){
}
}
var f=fn();//fn执行形成的这个私有的作用域就不能再销毁了

也就是像上面这段代码,fn函数内部的私有作用域会被一直占用的,发生了内存泄漏。所谓内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。闭包不能滥用,否则会导致内存泄露,影响网页的性能。闭包使用完了后,要立即释放资源,将引用变量指向null。

接下来我们看下有关于内存泄漏的一道经典面试题:

1
2
3
4
5
6
7
8
9
10
11
12
13
 function outer(){
var num=0;//内部变量
return function add(){//通过return返回add函数,就可以在outer函数外访问了
num++;//内部函数有引用,作为add函数的一部分了
console.log(num);
};
}
var func1=outer();
func1();//实际上是调用add函数, 输出1
func1();//输出2 因为outer函数内部的私有作用域会一直被占用
var func2=outer();
func2();// 输出1 每次重新引用函数的时候,闭包是全新的。
func2();// 输出2

闭包的作用和用处

  • 可以读取函数内部的变量
  • 让这些变量始终保持在内存中
  • 封装对象的私有属性和私有方法