美文网首页
JS——闭包是什么?用处如何?

JS——闭包是什么?用处如何?

作者: T_saber | 来源:发表于2018-04-07 19:56 被阅读0次

    简介作用域链、执行上下文概念

    处于活动状态的执行上下文环境只有一个。

    作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

    作用域在函数定义时就已经确定了。而不是在函数调用时确定。

    作用域只是一个“地盘”,一个抽象的概念,其中没有变量。要通过作用域对应的执行上下文环境来获取变量的值。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。所以,作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了。

    以上代码中:第13行,fn()返回的是bar函数,赋值给x。执行x(),即执行bar函数代码。

    闭包应用的两种情况——函数作为返回值函数作为参数传递

    一个自调用函数会在定义的时候被自动调用,因为其后面跟着一对括号(方法执行的方式)。

    第一,函数作为返回值

    bar函数作为返回值,赋值给f1变量。

    第二,函数作为参数被传递

    fn函数作为一个参数被传递进入另一个函数,赋值给f参数。

    要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,切记切记——其实这就是所谓的“静态作用域”。

    有些情况下,函数调用完成之后,其执行上下文环境不会接着被销毁。这就是需要理解闭包的核心内容。

    第三步,执行完第17行,fn()调用完成。按理说应该销毁掉fn()的执行上下文环境,但是这里不能这么做。注意,重点来了:因为执行fn()时,返回的是一个函数。函数的特别之处在于可以创建一个独立的作用域。而正巧合的是,返回的这个函数体中,还有一个自由变量max要引用fn作用域下的fn()上下文环境中的max。因此,这个max不能被销毁,销毁了之后bar函数中的max就找不到值了。因此,这里的fn()上下文环境不能被销毁,还依然存在与执行上下文栈中。——即,执行到第18行时,全局上下文环境将变为活动状态,但是fn()上下文环境依然会在执行上下文栈中。另外,执行完第18行,全局上下文环境中的max被赋值为100。如下图:

    第四步,执行到第20行,执行f1(15),即执行bar(15),创建bar(15)上下文环境,并将其设置为活动状态。

    执行bar(15)时,max是自由变量,需要向创建bar函数的作用域中查找,找到了max的值为10。这个过程在作用域链一节已经讲过。

    这里的重点就在于,创建bar函数是在执行fn()时创建的。fn()早就执行结束了,但是fn()执行上下文环境还存在与栈中,因此bar(15)时,max可以查找到。如果fn()上下文环境销毁了,那么max就找不到了。

    使用闭包会增加内容开销

    javascript除了全局作用域之外,只有函数可以创建的作用域

    堆:队列优先,先进先出;由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

    栈:先进后出;动态分配的空间 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。 

    基本类型:Undefined、Null、Boolean、Number 和 String,这5中基本数据类型可以直接访问,他们是按照值进行分配的,存放在栈(stack)内存中的简单数据段,数据大小确定,内存空间大小可以分配。 

    引用类型:即存放在堆(heap)内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。 

    什么是内存泄漏?

    程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存。

    对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。

    不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。

    有些语言(比如 C 语言)必须手动释放内存,程序员负责内存管理。

    这很麻烦,所以大多数语言提供自动内存管理,减轻程序员的负担,这被称为"垃圾回收机制"(garbage collector)。

    垃圾回收机制

    标记清除:垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记,然后,它会去掉环境中的变量的标记和被环境中的变量引用的变量的标记,此后,如果变量再被标记则表示此变量准备被删除。

    引用计数:跟踪记录每个值被引用的次数,当声明一个变量并将一个引用类型的值赋给该变量时,这个值的引用次数就是1,如果这个值再被赋值给另一个变量,则引用次数加1。相反,如果一个变量脱离了该值的引用,则该值引用次数减1,当次数为0时,就会等待垃圾收集器的回收。

    这个方式存在一个比较大的问题就是循环引用,就是说A对象包含一个指向B的指针,对象B也包含一个指向A的引用。 这就可能造成大量内存得不到回收(内存泄露),因为它们的引用次数永远不可能是 0 。早期的IE版本里(ie4-ie6)采用是计数的垃圾回收机制,闭包导致内存泄露的一个原因就是这个算法的一个缺陷。

    我们知道,IE中有一部分对象并不是原生额javascript对象,例如,BOM和DOM中的对象就是以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数。因此,虽然IE的javascript引擎采用的是标记清除策略,但是访问COM对象依然是基于引用计数的,因此只要在IE中设计COM对象就会存在循环引用的问题!

    举个栗子:

    这段代码为什么会造成内存泄露?   执行这段代码的时候,将匿名函数对象赋值给el的onclick属性;然后匿名函数内部又引用了el对象,存在循环引用,所以不能被回收; 解决办法

    闭包的运用

    1. 匿名自执行函数

    我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,比如UI的初始化,那么我们可以使用闭包:

    我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此els,i,lng这些局部变量在执行完后很快就会被释放,节省内存!关键是这种机制不会污染全局对象。

    2. 实现封装/模块化代码

    3. 实现面向对象中的对象

    这样不同的对象(类的实例)拥有独立的成员及状态,互不干涉。虽然JavaScript中没有类这样的机制,但是通过使用闭包,

    我们可以模拟出这样的机制。还是以上边的例子来讲:

    Person的两个实例person1 和 person2 互不干扰!因为这两个实例对name这个成员的访问是独立的 。

    闭包的优缺点

    优点:

    可以让一个变量常驻内存 (如果用的多了就成了缺点

    避免全局变量的污染

    私有化变量

    缺点

    因为闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存

    引起内存泄露

    相关文章

      网友评论

          本文标题:JS——闭包是什么?用处如何?

          本文链接:https://www.haomeiwen.com/subject/itvyhftx.html