关于javascript闭包我想说几句

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

一.前言

在咱们的开发当中会常常用到闭包,由于他确确实实解决了一些问题,在面试的时候,也会常常被问到对闭包的理解,然而实际上闭包的概念并无统一的说法,但不论是怎么描述的,它的核心都是那样,下面就来探究一下闭包究竟是怎么理解的java

二.基础知识:

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.匿名函数的调用

  • 函数表达式调用法post

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

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

    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
    复制代码
  • 先赋值给一个变量再由变量调用spa

    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高级程序设计》

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

--《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》是怎么描述的

--《你不知道的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.闭包的优势和缺点

  • 优势

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

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

  • 缺点

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

4.闭包缺点的解决方案

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

5.闭包的常见形式

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

形式一:

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

形式二:

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

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

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

//打印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);
}
复制代码
相关文章
相关标签/搜索