关于js闭包我想说几句

声明:部份内容参考文章 Ice-shou:闭包详解一javascript

一.什么是闭包?

如下是三本比较权威的书对闭包的解释java

《JavaScript高级程序设计》bash

闭包是指有权访问另外一个函数做用域中的变量的函数;闭包

《JavaScript权威指南》异步

从技术的角度讲,全部的JavaScript函数都是闭包:它们都是对象,它们都关联到做用域链。函数

《你不知道的JavaScript》post

当函数能够记住并访问所在的词法做用域时,就产生了闭包,即便函数是在当前词法做用域以外执行。性能

说明ui

要搞懂什么是闭包,必需要先搞明白如下几点基础知识spa

二.基础知识:

1.变量的做用链

  • JavaScript变量有两种:全局变量,局部变量

  • 局部变量做用域通常在函数里面,在函数以外的视为全局变量

  • 通常来讲,在函数里面能够访问全局的变量,在函数外面不能够访问函数里面的变量

  • Javascript存在“链式做用域”结构(chain scope),这里的链式做用域能够理解为函数嵌套,子对象会一级一级地向上寻找全部父对象的变量。因此,父对象的全部变量,对子对象都是可见的,反之则不成立.

    var str1="hello";//全局变量
    function fun(){
    	var res="i am coming";//fun()内的局部变量
    }
    function funa(){
    	var str2=" world";//funa()内的局部变量
    	function funb(){
            function func(){
                console.log(str1);//hello
                console.log(str2);//world
    			console.log(res);//报错,undefined
            }
    		func();
    	} 
    	funb();
    }
    funa();
    console.log(str2);//报错
    复制代码

2.匿名函数的调用

  • 函数表达式调用法

    var exc=function(){
        console.log("hello world");
    }
    exc();
    复制代码
  • 自调用

    (function(){
          console.log("hello world");
    })();
    复制代码
  • 逐层调用

    function fun1(){
        var a=0;
        console.log(a);
        return function(){
            a++;
            console.log(a);
        }
    }
    //注意这种调用的结果:
    fun1();
    fun1()();
    fun1()();
    输出
    0
    0
    1
    0
    1
    //解释:每一次先执行fun1(),a都会初始化为0,再执行匿名函数,a++获得1
    复制代码
  • 先赋值给一个变量再由变量调用

    function fun1(){
        var a=0;
        console.log(a);
        return function(){
            a++;
            console.log(a);
        }
    }
    //注意这种调用的结果:
    var res=fun1();
    res();
    res();
    res();
    输出
    0
    1
    2
    3
    //解释:fun1()只执行一次,因此a=0只执行一次,之后每次执行res()是在执行匿名函数,每执行一次,a自增一次
    复制代码

三.分析闭包

闭包是指有权访问另外一个函数做用域中的变量的函数;《JavaScript高级程序设计》

从技术的角度讲,全部的JavaScript函数都是闭包:它们都是对象,它们都关联到做用域链。

《JavaScript权威指南》

function fn1() {
	var str = 'hello,world';
	function fn2() {
		console.log(str);//能够访问fn1()函数
	}
	fn2();
}
fn1();
复制代码

说明

对于上面两本书对闭包的定义都比较迷,按照定义,fn2()函数就是一个闭包,,可是这就是闭包了吗?实际上并不明显,咱们看一个更加明显的例子...

function fn1() {
	var str = 'hello world';
	function fn2() {
		console.log(str);
	}
	return fn2;
}
var fn3 = fn1();
fn3();
复制代码

说明

  • fn2的词法做用域能访问fn1的做用域

  • 将fn2当作一个值返回

  • fn1执行后,将fn2的引用赋值给fn3

  • 执行fn3,输出了变量str

当函数能够记住并访问所在的词法做用域时,就产生了闭包,即便函数是在当前词法做用域以外执行。

《你不知道的JavaScript》

function fun1(){
    var a=0;
    console.log(a);
    return function(){
        a++;
        console.log(a);
    }
}
//注意这种调用的结果:
var res=fun1();
res();
res();
res();
输出
0
1
2
3
复制代码

说明

  • 第一次执行var res=fun1(),把匿名函数的引用传给了变量res

  • 每次执行res()即在调用匿名函数,注意调用的位置是在匿名函数做用域以外

  • 通常来讲局部变量在函数执行以后就会别垃圾回收机制回收,可是调用res()以后变量a并无被回收,每执行一次res(),a的值自增一次

  • res()函数能够记住并访问原来所在的词法做用域

四.总结

1.什么是闭包

  • 一个能够访问其余做用域的变量的函数
  • 闭包可以可以记住并访问原来所在的词法做用域

2.闭包的优势和缺点

  • 优势

    • 实现了能够访问其余做用域变量,而且避免了全局变量对自身词法做用域变量的污染

    • 能够把局部变量(自身做用域的变量)驻留在内存中一直保存着上一次执行的值,不会被垃圾回收机制回收,从而避免使用全局变量

  • 缺点

    • 局部变量一直驻留在内存中不会被回收,致使内存被爆满,影响程序性能

3.闭包缺点的解决方案

  • 建议在很是有必要的时候才使用闭包
  • 使用完变量肯定再也不使用, 将null赋值给变量,var a = null;

4.闭包的常见形式

上面我用了很多的例子来解释什么是闭包,不难发现闭包存在的形式,就是一个知足闭包定义的各类条件的函数,并且常以匿名函数的形式出现(注意:并非匿名函数都是闭包,两者不能等同)

function fun1(){
    return function(){
        //闭包主体
    }
}
var res=fun1();
res();//闭包函数调用
复制代码
(function(i){
    //闭包主体
})(i);//闭包函数自调用
复制代码

5.闭包的应用的典型案例

  • 异步程序中避免因执行时间不一致致使变量丢失

    //打印1-10
    for (var i = 1; i <= 10; i++) {
    	setTimeout(function () {
    		console.log(i);
    	}, 1000);
    }
    //结果打印了10个11
    复制代码

    缘由说明

    • i是声明在全局做用中的,定时器中的匿名函数也是执行在全局做用域中,打印i值得时候向上逐层寻找变量
    • for循环执行的速度要远比定时器执行的速度快,因此,定时器还将来得及打印i,for循环已经循环完毕,此时i的值是11,因此定时器打印出来的就是11

    解决

    上例很明显就是在匿名函数里面访问全局变量,因为异步缘由致使并未能准确打印出全局变量的值,因此解决方案就是循环i的时候,把i保存在私有做用域中而且一直保存,使用闭包来实现

    for (var i = 1; i <= 10; i++) {
    	(function (j) {
    		setTimeout(function () {
    			console.log(j);
    		}, 1000);
    	})(i);
    }
    复制代码
相关文章
相关标签/搜索