关于闭包

闭包定义:

A closure is the combination of a function and the lexical environment within which that function was declared. --MDN

A closure is the local variables for a function - kept alive after the function has returned . --javascriptkit

函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为“闭包”--JavaScript权威指南


1.闭包的产生:当函数可以记住并访问所在的词法作用域时(即使函数是在当前词法作用域之外执行),就产生了闭包

关于作用域链

  • 函数在执行的过程中,先从自己内部找变量
  • 如果找不到,再从创建当前函数所在的作用域词法作用域去找, 以此往上
  • 注意找的是变量的当前的状态

2.什么是闭包

垃圾回收和闭包

每次调用 js 函数时,都会为之创建一个新的变量对象用来保存局部变量,把这个对象添加至作用域链中。当函数返回时,就从作用域链中将这个绑定的变量对象删除。如果不存在嵌套的函数,也没有其他引用指向这个绑定对象,他就会被当做垃圾回收掉。如果定义了嵌套的函数,每个嵌套的函数都各自对应一个作用域链,并且这个作用域链指向一个变量绑定对象。但如果这些嵌套的函数对象在外部函数中保存下来,那么他们也会和所指向的变量绑定对象一样当做垃圾回收。但是如果这个函数定义了嵌套的函数,并将它作为返回值返回或者存储在某处的属性里,这时就会有一个外部引用指向这个嵌套的函数,他就不会被当做垃圾回收,并且他所指向的变量绑定对象也不会被当做垃圾回收。

原文分三段 代码理解
1)每次调用 js 函数时,都会为之创建一个新的变量对象用来保存局部变量,把这个对象添加至作用域链中。当函数返回时,就从作用域链中将这个绑定的变量对象删除

 var foo = function () {
        var i = 20
        i++
        return i
    }
    console.log(foo())//21
    console.log(foo())//21

变量没有得到保存
2)如果定义了嵌套的函数,每个嵌套的函数都各自对应一个作用域链,并且这个作用域链指向一个变量绑定对象。但如果这些嵌套的函数对象在外部函数中保存下来,那么他们也会和所指向的变量绑定对象一样当做垃圾回收。

var foo = (function car () {
         i = 20
        function b() {
            i++
            return i
        }
        b()
            return i
        })()
     console.log(foo)//21

    console.log(foo)//21

函数b的词法作用域可以访问foo()的内部作用域,但并没有将b所引用的函数对象本身作为返回值。变量绑定对象没有被"记住"
3)但是如果这个函数定义了嵌套的函数,并将它作为返回值返回或者存储在某处的属性里,这时就会有一个外部引用指向这个嵌套的函数,他就不会被当做垃圾回收,并且他所指向的变量绑定对象也不会被当做垃圾回收。

var foo = (function car () {
         i = 20

       return  function b() {
            i++
            return i

        }


        })()

     console.log(foo())//21

    console.log(foo())//22

一般来说,在执行car()之后,car()的内部作用域会被销毁。在这个例子里,内部作用域在函数执行完成后依然存在,因为b一直在引用,使得该作用域一直存活。b依然持有对该作用域的引用

而该引用就叫做闭包

如果将函数(访问他们各自的词法作用域)当做第一级的值类型并到处传递,你就会看到闭包在这些函数中的应用。在定时器,事件监听器。Ajax请求,跨窗口通信,Web Workers或者其他任何异步(或同步)任务中,只要使用了回调函数,实际上就是在使用闭包

2.闭包是为了?

1)封装数据
2)暂存数据
因此闭包可以使得函数继续访问定义时的词法作用域

var fn
 function foo() {
     var a = 2
     function baz() {
         console.log(a)
     }
    fn = baz//将baz分配给全局变量


 }
 function bar() {
     fn()
 }
 foo()
    bar()//2

3.闭包是什么

函数连同它作用域链上的要找的这个变量,共同构成闭包

//1
var fnArr = []
    for (var i = 0; i < 10; i ++) {
       fnArr[i] = (function(j){
            return   function(){
                return j
            }
        })(i)
    }
    console.log( fnArr )

IIFE在每次迭代中都会创建一个词法作用域。i作为参数传入,匿名函数有自己的变量j,用来在每个迭代中储存i的值。
这样子一来就会产生十个闭包

1.png

2.png
3.png
//2
 var Car = (function(){
        var speed = 0;
        function set(s){
            speed = s
        }
        function get(){
            return speed
        }
        function speedUp(){
            speed++
        }
        function speedDown(){
            speed--
        }
        return {
            set: set,
            get: get,
            speedUp: speedUp,
            speedDown: speedDown
        }
    })()
    Car.set(30)
    console.log(Car.get())
    Car.speedUp()

    console.log(Car.get())
    Car.speedDown()
    console.log(Car.get())

几个函数作为Car对象内部的属性,函数连同作用域链上的变量绑定对象构成闭包

浏览器检查,Source里面watch Car对象


2 (1).png
2 (2).png

(这里不太懂是有几个闭包?)

4闭包和IIFE的关系

立即执行函数是最常用的创建可以被封闭起来的闭包的工具,严格来讲立即执行函数并不是闭包,虽然他的确创建了闭包。

参考自
你不知道的JavaScript
权威指南
http://www.javascriptkit.com/javatutors/closures.shtml
https://segmentfault.com/q/1010000005007819?_ea=754717
http://book.jirengu.com/fe/%E5%89%8D%E7%AB%AF%E5%9F%BA%E7%A1%80/Javascript/%E9%97%AD%E5%8C%85.html
https://www.cnblogs.com/linwx/articles/7689019.html
https://segmentfault.com/q/1010000013049434/

推荐阅读更多精彩内容

  • 本文章著作权归饥人谷_Lyndon和饥人谷所有,转载请注明出处。 闭包对于我而言是一个难点,但闭包又是一个很有用的...
    HungerLyndon阅读 571评论 1 3
  • 卡尔维诺中文站留言板这个帖子专门用作卡尔维诺中文站的留言板,欢迎大家留言和提问。...阮一峰2007-01-04T...
    舟渔行舟阅读 38评论 0 1
  • 今天看到了一段关于闭包的文章,感觉挺好 挺有用的 我拿过来和大家分享一下,希望看到的小伙伴都能彻底理解闭包 =>...
    your_own_king阅读 30评论 0 2
  • 1.什么是闭包? 有什么作用 闭包指有权访问另一个函数作用域的变量的函数。创建闭包的常见方式 是 在一个函数...
    JunVincetHuo阅读 258评论 0 1
  • 作用域和闭包是 JavaScript 最重要的概念之一,想要进一步学习 JavaScript,就必须理解 Java...
    劼哥stone阅读 590评论 1 13