概述一下什么是 JS 中的闭包,以及它的优缺点的一个简单总结 :
程序员文章站
2022-04-12 09:53:25
...
在 JS 中,闭包 是一个神奇的存在,这篇文章概述一下 JS 中的闭包的 概念,以及它的 优缺点的一个简单总结 :
- 闭包概念 :
闭包是 js 中的一大特色,也是一大难点。简单来说,所谓闭包就是说,能够读取其他函数内部变量的函数;或 简单理解为定义在一个函数内部的函数,内部函数持有外部函数内变量的引用。
通常情况下,变量作用域两种:全局变量、局部变量。js 中函数内部可以读取全局变量,函数外部不能读取函数内部的局部变量。要想从外部读取函数内部的变量,就会用到闭包。
- 闭包特性 :
- 函数嵌套函数;
- 函数内部可以引用函数外部的参数和变量;
- 参数和变量不会被垃圾回收机制回收;
- 闭包的使用场景 :函数作为返回值
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
========================================================*/
- 闭包优点 :
- 希望一个变量长期存储在内存中;
- 避免全局变量的污染;
- 私有成员的存在;
- 闭包缺点 :
- 被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,解决方法是可以在使用完变量后手动为它赋值为null;(常驻内存,增加内存使用量,使用不当会很容易造成内存泄露。)
- 其次由于闭包涉及跨域访问,所以会导致性能损失,我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响;