闭包的概念
说到闭包,这是js不得不提的一个特性,很多传统语言都不具备这样的特性,比如JAVA、C等等.首先基本上所有的编程语言都有类似的特性,局部方法可以访问外部父类方法的属性,也就是说,子类或子方法可以访问父类的资源。闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。闭包说白了就是函数的嵌套,内层的函数可以使用外层函数的所有变量.
function outerFun()
{
var a = 0;
function innerFun()
{
a++;
console.log(a);
}
return innerFun; //注意这里
}
var obj=outerFun();
obj(); //结果为1
obj(); //结果为2
var obj2=outerFun();
obj2(); //结果为1
obj2(); //结果为2
// 什么是闭包:
// 当内部函数在定义它的作用域的外部被引用时,就创建了该内部函数的闭包,
// 如果内部函数引用了位于外部函数的变量,当外部函数调用完毕后,这些变量在内存不会被释放,因为闭包需要它们.
每个函数的执行,都会创建一个与该函数相关的函数执行环境,或者说是函数执行上下文。这个执行上下文中有一个属性 scope chain(作用域链指针),这个指针指向一个作用域链结构,作用域链中的指针又都指向各个作用域对应的活动对象。正常情况,一个函数在调用开始执行时创建这个函数执行上下文及相应的作用域链,在函数执行结束后释放函数执行上下文及相应作用域链所占的空间。
function outerFun1()
{
var a = 0;
console.log(a);
}
var a = 4;
outerFun1();
console.log(a);
//结果是 0,4。因为在函数内部使用了var关键字维护a的作用域在outFun1()内部.
function outerFun2()
{
//没有var
a = 0;
console.log(a);
}
var a = 4;
outerFun2();
console.log(a);
// 结果为 0,0 真是奇怪,为什么呢?
// 作用域链是描述一种路径的术语,沿着该路径可以确定变量的值 .
// 当执行a=0时,因为没有使用var关键字,因此赋值操作会沿着作用域链到var a=4;并改变其值.
闭包的特性
- 函数嵌套函数
- 函数内部可以引用外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
闭包的好处
- 希望一个变量长期驻扎在内存中
- 避免全局变量的污染
闭包的用途
- 可以读取函数内部的变量
- 让这些变量的值始终保持在内存中
- 模拟面向对象的特性
JavaScript闭包的本质
闭包的本质源自两点,词法作用域和函数当作值传递。
函数当作值传递,即所谓的first class对象。就是可以把函数当作一个值来赋值,当作参数传给别的函数,也可以把函数当作一个值 return。一个函数被当作值返回时,也就相当于返回了一个通道,这个通道可以访问这个函数词法作用域中的变量,即函数所需要的数据结构保存了下来,数据结构中的值在外层函数执行时创建,外层函数执行完毕时理因销毁,但由于内部函数作为值返回出去,这些值得以保存下来。而且无法直接访问,必须通过返回的函数。这也就是私有性。
闭包是词法作用域和函数作为First-class的共同体现。那什么是闭包呢,一言以蔽之:一个持有外部环境变量的函数就是闭包。
闭包的垃圾回收
一般函数执行完毕后,局部活动对象就被销毁,内存中仅仅保存全局作用域。但闭包的情况不同!
function aaa() {
var a = 1;
return function(){
console.log(a++);
};
}
var fun = aaa();
fun();// 1 执行后 a++
fun();// 2 //这说明a++后,还存在,还一直在内存中
fun = null;//a被回收!!
闭包会使变量始终保存在内存中,如果不当使用会增大内存消耗。
javascript的垃圾回收原理
- 在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;
- 如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。
[Lua]函数闭包