这是我最烦的东西,没有之一!一万头草泥马狂奔而过!
学习闭包我们要清楚函数作用域、内存回收机制、作用域继承。
作用域我们可以认为它是一个封闭的盒子,只让它在这个盒子里面进行操作,也可以称这个盒子为独立作用域。在js中,一个函数要执行时就会在内存里面创建一个独立作用域————封闭的盒子。
比如在函数中第一一个变量,只能在函数这个独立作用域中使用(也就是封闭的盒子)。只要跳出这个作用域,就找不到该变量了。
而且函数执行完毕之后,这个独立作用域或(封闭的盒子)就会删除。有一种情况下这个封闭的盒子是不会删除的,那就是“闭包”,后面会讲到。
内存回收机制就是不在用到的内存空间,系统会自动进行清理出空间提供给其他程序使用。那回收的前提是什么呢?
内部函数引用外部的函数的变量,外部函数执行完毕,作用域也不会删除。从而形成了一种不删除的独立作用域。
某一个变量或者对象被引用,因此在回收的时候不会释放它,因为被引用代表着被使用,回收机制不会对正在引用的变量或对象进行回收的。
所谓的作用域继承,就像是儿子可以继承父亲的财产一样。比如我这里有一个大的盒子作为一个父级的作用域,然后在这个大的盒子里边放一个小的盒子,作为子作用域。我们规定可以在小盒子中获取到大盒子中的东西,大盒子不能获取小盒子里的东西就称为作用域继承。
在 JS 中,道理是一样的,在一个函数里边我们再声明一个函数,内部函数可以访问外部函数作用域的变量,而外部的函数不能获取到内部函数的作用域变量。
那好,上边的这几个概念理解了之后,什么是闭包对你来说已经不是什么问题。
什么是闭包,那就是在一个函数里边再定义一个函数。这个内部函数一直保持有对外部函数中作用域的访问(小盒子可以一直访问大盒子但大盒子不能访问小盒子)。
函数执行,形成一个独立作用域,保护里边的私有变量不受外界的干扰,除了保护私有变量外,还可以存储一些内容,这样的模式叫做闭包。
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
作用是:通过一系方法,将函数内部的变量(局部变量)转化为全局变量
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
- function f1(){
- var n=999;
- nAdd=function(){
- alert(n+=1);
- }
- function f2(){
- alert(n);
- }
- return f2;
- }
- var result=f1();
- result(); //999
- nAdd();//1000
- result(); //1000
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。 其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个操控者,可以在函数外部对函数内部的局部变量进行操作。
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
- function f1() {
- var a = 0;
- return function () {
- return a+5;
- }
- }
-
- var f=f1();
- console.log(f);
- //打印结果:打印出来一个函数,而且正好是内层函数
- ƒ () {
- return a+5;
- }
-
- console.log(f());
- //打印结果:5
如果我们碰到这样的例子,我们记住一点,外层函数只要不被调用第二次,那么外层函数的参数或者变量的值就不会被冲刷,也就是说对于内层函数而言,外层函数的变量可以看成“全局变量”。
- function f2() {
- var a = 0;
- return function () {
- return a++;//a++称为“后增量”,++a称为“前增量”
- }
- }
- f=f2();
- console.log(f());
- console.log(f());
- console.log(f());
- f=f2();
- console.log(f());
- console.log(f());
- console.log(f());
-
- 输出结果为:
- 0
- 1
- 2
- 0
- 1
- 2
- function f3() {
- var a = 0;
- return function () {
- return ++a;
- }
- }
-
- console.log(f3()());
- console.log(f3()());
- console.log(f3()());
- 打印结果:
-
- 1
- 1
- 1
这个例子我们直接用图解来看一下:
这个不就相当于外层函数调用了3次,所以a也被重置成0三次。