欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

概述一下什么是 JS 中的闭包,以及它的优缺点的一个简单总结 :

程序员文章站 2022-04-12 09:53:25
...

JS 中,闭包 是一个神奇的存在,这篇文章概述一下 JS  中的闭包的 概念,以及它的 优缺点的一个简单总结 :


  • 闭包概念 :

闭包是 js 中的一大特色,也是一大难点。简单来说,所谓闭包就是说,能够读取其他函数内部变量的函数;或 简单理解为定义在一个函数内部的函数,内部函数持有外部函数内变量的引用

通常情况下,变量作用域两种:全局变量、局部变量。js 中函数内部可以读取全局变量,函数外部不能读取函数内部的局部变量。要想从外部读取函数内部的变量,就会用到闭包。

  • 闭包特性 :
  1. 函数嵌套函数;
  2. 函数内部可以引用函数外部的参数和变量;
  3. 参数和变量不会被垃圾回收机制回收;
  • 闭包的使用场景 :函数作为返回值
function print(){
    var name="dov"
    return function(){
        return name;
  }
}
var b = print();
console.log(b())//输出 dov

/*===============================
在这段代码中,a()中的返回值是一个匿名函数,这个函数在a()作用域内部,所以它可以获取a()作
用域下变量name的值,将这个值作为返回值赋给全局作用域下的变量b,实现了在全局变量下获取到局
部变量中的变量的值
=================================*/
function fn(){
    var num = 3;
    return function(){
        var n = 0;
        console.log(++n)
        console.log(++num)
    }
}
var fn1 = fn();
fn1()//输出 1,4
fn1()//输出 1,5

/*==================================================
一般情况下,在函数fn执行完后,就应该连同它里面的变量一同被销毁,但是在这个例子中,匿名函
数作为fn的返回值被赋值给了fn1,这时候相当于fn1=function(){var n = 0 ... },并且匿名函数
内部引用着fn里的变量num,所以变量num无法被销毁,而变量n是每次被调用时新创建的,所以每次
fn1执行完后它就把属于自己的变量连同自己一起销毁,于是乎最后就剩下孤零零的num,于是这里就
产生了内存消耗的问题
===================================================*/
  • 定时器与闭包 :
//一个 for 循环,按顺序打印出当前的循环次数
for(var i=0; i< 5; i++){
    setTimeout(function(){
        console.log(i + '')
    },100);
}

/*==============================================
按照预期它应该依次输出1 2 3 4 5,而结果它输出了五次5,这是为什么呢?因为setTimeout()函
数要等执行完函数调用栈中的代码,然后立即调用定时器。这是因为,我们的定时器都被放在了一个
被称为队列的数据结构中,等待上下文的可执行代码运行完毕后,才开始运行定时器,也就是定时器
才刚开始同时计时。所以在定时器的方法执行的时候,变量i已经变成了5,所以输出的全部是5。
==============================================*/

解决上述问题的方法 1 :

ES6 中提出了一个新的关键字 let ,就可以声明一个仅对当前 “{}” 内部有作用的变量。把 for 循环里面的 var 变成 let, 实现预期结果;

解决上述问题的方法 2 :

引入闭包来保存变量 i,将 setTimeout 放入立即执行函数中,将 for 循环中的循环值 i 作为参数传递,100毫秒后同时打印出1 2 3 4 5, 如果我们想实现每隔 100毫秒 分别依次输出数字  i*100;

for(var i=1; 1<5; i++){
    (function(i){
        setTimeout(function(){
            console.log(i)
        },i*100)
    })(i)
}
  • 闭包的使用场景 :闭包作为参数传值 
var num =15;
var fn1=function(x){
    if(x>num){
        console.log(x)
    }
}
void function(fn2){
    var num = 100;
    fn2(30)
}(fn1)//输出 30

/*=======================================================
在这段代码中,函数fn1作为参数传入立即执行函数中,在执行到fn2(30)的时候,30作为参数传入
fn1中,这时候if(x>num)中的num取的并不是立即执行函数中的num,而是取创建函数的作用域中的
num这里函数创建的作用域是全局作用域下,所以num取的是全局作用域中的值15,即30>15,打印30
========================================================*/
  • 闭包优点 :
  1. 希望一个变量长期存储在内存中;
  2. 避免全局变量的污染;
  3. 私有成员的存在;
  • 闭包缺点 :
  1. 被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,解决方法是可以在使用完变量后手动为它赋值为null;(常驻内存,增加内存使用量,使用不当会很容易造成内存泄露。)
  2. 其次由于闭包涉及跨域访问,所以会导致性能损失,我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响;

 

相关标签: JS js javascript