怎样理解JavaScript闭包

怎样理解JavaScript 闭包,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

公司主营业务:成都做网站、成都网站建设、成都外贸网站建设、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。成都创新互联公司是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。成都创新互联公司推出兰州免费做网站回馈大家。

 深入剖析 JavaScript 闭包

    什么是闭包?
❝  

一个函数和对其周围状态的引用捆绑在一起,这样的组合就是「闭包」.

通俗的说:一个内层函数可以访问外层函数的作用域 就叫 「闭包」

在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。

 
闭包的形成与变量的作用域以及变量的生命周期密切相关。
❞    

闭包的特性

❝  
  1. 函数嵌套函数

  2. 函数内部可以引用外部的参数和变量

  3. 参数和变量不会被垃圾回收机制回收

❞    

闭包的优缺点

❝  

优点:

可以设计私有的方法和变量

❞  

「缺点」

常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。

❞  

「一般函数执行完毕后,局部活动对象就被销毁,内存中仅仅保存全局作用域。」

❞  
 

关于 变量

 
变量的作用域
❝  

变量的作用域:变量的有效范围。

在实际开发中,我们经常遇到的是 「函数中声明的变量作用域。」

❞  
var a = '闭包';

function getValue(){
    var a = '函数局部作用域'
    console.log(a)
}

getValue()  //函数局部作用域

 
❝  

当在全局声明了一个同名变量,在函数内部也声明了一个同名变量,函数优先访问函数作用域中的变量。

❞  
 
函数作用域
❝  

函数作用域:在函数内部可以访问到函数外部变量,而在函数外部的变量不可以访问函数内部的变量。

 
为什么呢?

「因为当在函数中搜索一个变量的时候,如果函数内部没有这个变量的声明,那么它会随着代码的执行环境创建的作用域往外层逐层搜索,直到搜索到全局变量为止。」

变量的搜索是从内到外搜索的。

❞  
function getData() {
    var str = "闭包练习";
    var fun = function(){
        var innerStr = '内部变量'
    }
    console.log(innerStr) 
     //innerStr is not defined 函数外层是访问不到 函数内层变量的
}
getData()
   
变量的生存周期
❝  

对于 「全局变量」,它的生存周期是永久的的,除非主动销毁变量。

而对于 「函数局部变量」,当函数执行完毕,局部变量也就销毁了。

❞  
 
栗子 1




    
    
    
    Document



    
1

    
2

    
3

    
4


    



 
❝  

给每个 div 增加点击事件,当点击 div 时,弹出它对应的索引值。

现在无论点击哪个 div ,它 弹出的 都是 4 。

 
为什么呢?

「因为 div 点击事件 是被 异步触发的,当事件被触发的时候,循环已经执行完,此时的 i 的 变量值 为 4。」

 
如何解决 点击每个div 弹出对应的i 值呢 ?

**可以借用 闭包, 把每次循环的 i 保存起来,当执行点击事件时,它会从内到外 搜索变量的作用域,它会优先搜索到 闭包环境环境的 i **

❞  
 # 闭包解决办法   

   
栗子 2
var num = 1;
function getValue(){
    var num = 0;
    return function(){
        num++
        console.log(num)
    }
}

var s = getValue()
s()
s()
// 1 2 
 
❝  

按常理思路来:函数执行完毕,num = 1 销毁,变为初始值 num = 0 ,变量在函数中作用域从内到外逐层搜索。

前面也说到了,当函数执行完,局部变量也跟着销毁了,那为什么会 输出 2 呢 ?

这里 涉及到 垃圾回收机制引用计数问题

[关于垃圾回收]   https://blog.csdn.net/zhouziyu2011/article/details/61201613

简述:

「当声明了一个变量并将一个引用类型值赋给该变量时,则该值的引用次数就是1;如果同一个值又被赋给另一个变量,则该值的引用次数加1;如果包含对该值引用的变量又取得了另外一个值,则该值的引用次数减1。当该值的引用次数变为0时,则可以回收其占用的内存空间。当垃圾回收器下一次运行时,就会释放那些引用次数为0的值所占用的内存。」

❞  

「解答」

第一次执行 s() 时,num = 1

第二次 执行 s() 时, 由于 引用的时第一次 s () 的变量num=1,num 没有被销毁,固然在 num = 1 的基础上 再 加 1 。

❞  

「注意」

如果没有使用同样引用的话,那么多次调用,都是同样的值,因为没有记录引用值。

函数在执行完毕,num = 1 被销毁掉了,初始为 0

❞  
var num = 1;
function getValue(){
    var num = 0;
    return function(){
        num++
        console.log(num)
    }
}

getValue()()
getValue()()
// 0 0
   

????闭包的作用

❝  

闭包的注意作用为这两项:

  1. 「可以读取函数内部的变量」
  2. 「可以变量的值始终保持在内存中」
❞  
 
栗子
function f2(){
    let num = 0;
    addNum = function(){
        num++
    }
    function f3(){
        console.log(num)
    }
    return f3
}

var a = f2()
a()
addNum()
a()
// 0  1 
 
❝  

结果为 0 1

函数在执行完毕,局部变量也跟着销毁, 结果 不应该是  0  0 吗 ?

其实a() 相当于 是 f3() 的闭包函数,它被执行了两次。

  • 第一次 执行 a() 时, 结果为 0 , 很好理解。
  • 第二次   执行的      f2() 函数内部的      addNum 函数,发现没这个匿名函数赋值给一个变量,而且这个变量没加        var / let , 那么它此时的作用域为      全局 ,保存在内存当中。执行      addNum 时它访问的      f2() 函数内部的局部变量      num , 此时,      addNum 的存在依赖于      f2,因此      f2 也在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
  • 第三次        执行        a()       时, 因为      num       已存在内存中,而      值为1

最终输出结果:0 , 1

❞  

    闭包注意
  1. 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
  2. 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
❞  

看完上述内容,你们掌握怎样理解JavaScript 闭包的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注创新互联行业资讯频道,感谢各位的阅读!


网站栏目:怎样理解JavaScript闭包
网页链接:http://ybzwz.com/article/gjshop.html