js闭包?刚看到这个名词的时候真的是觉得好高大上的赶脚,因为。。。真的是一脸懵,不知道这是个什么鬼。但是我们在JavaScript中又不可避免地要用到闭包,因此,后来研究了一下,又查了下资料,发现人家只是取了个高大上的名字而已,其实还是很接地气的。
一. 变量的作用域
要了解闭包,首先就要先说一下javascript的变量作用域问题,变量的作用域无非就是两种,全局变量和局部变量。简单的来说,全局变量就是定义在函数外部的变量,而局部变量则就是定义在函数内部的变量。
JavaScript语言的特殊之处就在于函数内部可以直接读取全局变量。
js代码:
var a= 88;
function fn(){
alert(a);
}
fn(); //88
而在函数外部则无法读取函数内部的值
js代码:
function fn(){
var a= 88;
}
fn();
alert(a); //error (a is not defined)
需要注意的是,此时在函数内部定义变量的时候要使用var 命令,否则该变量就被定义为了一个全局变量
js代码:
function fn(){
a= 88;
}
fn();
alert(a) //88
二.怎么从外部获取局部变量的值
从上面的例子我们可以看出,在函数外部我们是无法获取函数内部的值的,但是,有时候我们又必须要用到函数内部的值,这个时候我们就要另辟蹊径。下面我们先来看一个例子。
js代码:
function fn1(){
a= 88;
function fn2(){
alert(a);
}
fn2(); //88
}
fn1();
在上面的代码中,函数fn2是包含在函数fn1内部的,那么函数fn1内部的所有的局部变量对于fn2来说都是可见的,但是,反过来就不行,函数fn2内部的变量对于fn1却是不可见的。这个就是javascript特殊的“链式作用域”结构。所谓链式作用域就是说相同的变量名会产生一个作用域链,访问该变量名称的事后首先找本层是否有该变量,如果没有去父层找,最终找到全局变量为止(如果全局变量也没有,那么会报错),也就是说,子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然fn2可以读取fn1内部的变量的值,那么我们只要把fn2作为一个返回值返回,不就可以得到函数fn1内部的变量的值了么。
function fn1(){
a= 88;
function fn2(){
alert(a);
}
return fn2;
}
var result = fn1();
console.log(result()); //88
在上述函数中,如果我们最后是console.log(result);那么打印的将是fn2这个函数,因此我们需要再运行fn2才能得到想要的结果,也就是console.log(result());
三.闭包的概念
其实在上面这个例子中,作为返回值返回的fn2就是一个闭包。
各种专业文献里面对闭包的定义十分的抽象,很难理解。其实简单来说,闭包就是能够读取其他函数内部变量的函数。由于在JavaScript语言中,只有函数内部的函数才可以读取局部变量,因此可以把闭包简单理解为“定义在函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
四. 闭包的用途
闭包最主要的用途有两个,其中一个就是上面所说的用于得到函数内部的局部变量。另一个用途就是把这些变量的值始终保存在内存中。
怎么来理解呢?下面我们来看一个例子
function fn1(){
a= 88;
function fn2(){
return a++;
}
return fn2;
}
var result = fn1();
console.log(result()); //88
console.log(result()); //89
一般的来说局部变量的值会被个函数在执行开始的时候,会给其中定义的变量划分内存空间保存,以备后面的语句所用,等到函数执行完毕返回了,这些变量就被认为是无用的了.对应的内存空间也就被回收了.下次再执行此函数的时候,所有的变量又回到最初的状态,重新赋值使用.
但是为什么上面的例子为什么变量却没有在运行过后被回收呢?其实因为js解释器在遇到函数定义的时候,会自动把函数和他可能使用的变量(包括本地变量和父级和祖先级函数的变量(自由变量))一起保存起来.也就是构建一个闭包,这些变量将不会被内存回收器所回收,只有当内部的函数不可能被调用以后(例如被删除了,或者没有了指针),才会销毁这个闭包,而没有任何一个闭包引用的变量才会被下一次内存回收启动时所回收.
五.闭包使用过程中需要注意的问题
(1)由于闭包会使得函数中的变量也就是局部变量始终都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,
(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
六.总结
综上所述。闭包就是一个函数引用另外一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。这是优点也是缺点,不必要的闭包只会徒增内存消耗!另外使用闭包也要注意变量的值是否符合你的要求,因为他就像一个静态私有变量一样。闭包通常会跟很多东西混搭起来,接触多了才能加深理解,这里只是开个头说说基础性的东西。