一、理解与记忆核心
二、闭包的理解与实现
首先
根据【闭包是一个访问了其他函数内部作用域的函数】的定义
在不知道闭包的情况下,当我们想要在函数外使用函数内部的变量时,我们通常会想到什么?
1.全局变量!!
将函数内的局部变量变成全局变量, 在全局作用域中声明,在函数中赋值,这样,只要调用过该函数,就可以在函数外部取到想要的值
//全局变量
var a;//变量声明,let和const声明的变量充其量只能算script块下的,没有挂到window全局,因此,这里我还是改成var
function fn(){
a=20;//变量赋值
}
fn();//函数调用
console.log(window.a);//取到函数内作用的结果
为了图片展示方便,以下图片中我还是使用node环境运行,但运行结果保证与浏览器中一致 还有一个更好玩的 每次调用都相当于对同一个全局变量进行了+1的操作,可以实现累积的效果
但是!! 很多时候,我们并不想声明一个全局变量,因为每次访问a都相当于访问window.a,这样看起来是不是感觉太占用window变量的资源了, 如果只想用局部变量但又想在函数外访问呢?
2.不想使用全局变量
如果直接在外部使用局部变量,结果就报错了!a is not defined
// 局部变量
function fn(){
let a=30;
}
fn();
console.log(a);
那应该怎么办呢? 两种思路,
2.1.直接将局部变量返回
将a返回,把a从函数里抛出,让外面的aa来接住它!
function fn(){
let a = 40;
return a;
}
let aa = fn();
console.log(aa);
除此之外,我还可以对a进行一番play之后再返回
2.2.暴露一个访问器,让外部间接访问
function fn(){
let a=40;
// 注意,get和set不要加let或var,这样才相当于挂载到了全局对象上,否则外部是无法访问get和set的
//这里的get相当于一个全局变量,被挂载到了全局对象上
get = function(){
return a;
};
}
fn();
let aa = get();
console.log(aa);
console.log(window.get());
同样,可以对a进行一番play之后再返回
但是,如果我想多次对a进行该操作呢?
3.对该局部变量进行多次累计操作
同样对两种方法进行测试
3.1.直接返回
从图中结果可以看出,由于每次函数执行完毕时,js会进行垃圾回收,将作用域清空回收,每次a都被重新赋值,因此不管我调用多少次,都只会返回一次调用的结果 如何解决呢? 通过1中全局变量的例子可以得到一些启发 如果我们在(父)函数中有一个局部变量,并且(父)函数中有另一个(子)函数来访问该局部变量,那我们每调用一次(子)函数,由于(子)函数内有对其外部的a变量的引用,就可以实现该局部变量a的累积变动,为了能在(父)函数外部能操作,将操作的(子)函数直接返回
而这,就是我们的主角闭包了,因此才说一般闭包表现为一个函数中返回另一个函数
3.2.暴露一个访问器,让外部间接访问
为什么这样就可以呢?其实这种方式也属于闭包的一种,因为这里的get和set也满足了闭包【访问了其他函数内部作用域】的定义 所以,也不一定是return一个函数才能产生闭包,只是大部分情况是这样
三、闭包的应用场景
1、例如定时器、事件监听以及Ajax请求等这类使用回调函数的,基本都利用到了闭包,使用定时器的例子,如防抖/节流:
// 防抖
const debounce = (fn,delayTime) => {
let timerId, result
return function(...args) {
timerId && clearTimeout(timerId)
timerId = setTimeout(()=>result=fn.apply(this,args),delayTime)
return result
}
}
// 节流
const throttle = (fn, delayTime) => {
let timerId
return function(...args) {
if(!timerId) {
timerId = setTimeout(()=>{
timerId = null
return result = fn.apply(this,args)
},delayTime)
}
}
}
2、IIFE(立即执行函数),这种函数比较特别,它拥有独立的作用域,不会污染全局环境,但是同时又可以防止外界访问内部的变量,所以很多时候会用来做模块化或者模拟私有方法 举个例子:
var global = '全局变量'
let Anonymous = (function() {
var local = '内部变量'
console.log(global) // 全局变量
})()
console.log(Anonymous.local) // local is not defined
=======分割线==============
var global = '全局变量'
let Anonymous = (function() {
var local = '内部变量'
console.log(global) // 全局变量
return {
afterLocal: local
}
})()
console.log(Anonymous.afterLocal) // 内部变量
3.计时器
function counter2(){
let time = 0;
return function(){
time++;
console.log(time);
if(time>10){
clearInterval(timer);
}
}
}
let count = counter2();
count();
count();
count();
4.如何在浏览器中查看闭包
function fn1(){
let a=11;
let b="bbb";
return function fn2(){
let c="c";
console.log(c);
console.log(a);
}
}
fn1()();
当运行到fn2内部时,就产生了闭包Closure,且里面只有引用到的外部函数变量a
四、总结一下
一、什么是闭包?
访问了其他函数内部作用域的函数
二、产生闭包的原因?
当前函数内保持对上层作用域的引用
三、闭包的优缺点?
优点:
- 可以让一个变量长期存储在内存中。
- 避免全局变量的污染。
缺点:
- 常驻内存,增加内存使用量。
- 使用不当会很容易造成内存泄露。
五、参考
juejin.cn/post/696642…
zhuanlan.zhihu.com/p/22486908
六、往期精彩
原型与原型链
this指向
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!