专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序员的那些事  ·  突发!o3-mini ... ·  2 天前  
OSC开源社区  ·  2024年中国开源模型:崛起与变革 ·  2 天前  
OSC开源社区  ·  Gitee邀您参与SBOM行业调研:共建可信 ... ·  3 天前  
码农翻身  ·  漫画 | ... ·  昨天  
程序猿  ·  清晰的、模块化的编码风格 ·  3 天前  
51好读  ›  专栏  ›  SegmentFault思否

社区精选|深入剖析 JavaScript 闭包

SegmentFault思否  · 公众号  · 程序员  · 2022-12-05 18:40

正文

今天小编为大家带来的是社区作者 程序员海军 的文章,让我们一起来深入剖析 JavaScript 闭包




导读目录


  • 什么是闭包

  • 闭包的特性

  • 闭包的优缺点

  • 闭包的作用

  • 闭包的注意点


什么 是闭包?


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

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

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

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


闭包的特性


  • 函数嵌套函数
  • 函数内部可以引用外部的参数和变量
  • 参数和变量不会被垃圾回收机制回收


闭包的优缺点


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

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

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


关于变量


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

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

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

html>
"en">


    "UTF-8">
    "X-UA-Compatible" content="IE=edge">
    "viewport" content="width=device-width, initial-scale=1.0">
    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 吗 ?






请到「今天看啥」查看全文