先看一个简单的需求:浏览器
这真的是一个简单的需求, 咱们能够立马写出来闭包
var counter = 0 function addCounter(){ counter++ console.log(counter); } addCounter() // 1 addCounter() // 2 addCounter() // 3
可是你有没有想过: 为何 counter
能够一直被累加 ?ide
这里就牵扯到了js中的 垃圾回收机制函数
所谓的垃圾回收, 就是肯定哪一个变量再也不使用, 而后释放它占用的内存, 这是一个周期性的检测学习
可是对于全局变量而言, 垃圾回收机制并不知道它何时再也不使用, 所以也就没法释放其占用的内存, 全局变量也就会一直被保存在内存当中, 这也就是咱们能够一直使用它的缘由prototype
其实从执行上下文的角度来看, 全局变量的声明是在全局上下文中进行的, 被一直保存在GO中, 而全局上下文是在咱们关闭浏览器时才会被销毁, 所以其余函数就能够经过其保存的上下文环境 (也就是做用域链), 到GO中引用countercode
这样, 第一个问题咱们就解决了blog
好, 咱们再来思考另一个问题, 若是另一个函数也用到了 counter
, 或者另一个开发者声明了一个一样的全局变量 counter
,那么这势必会影响上面累加器的运做, 由于 counter
的值被其余函数 / 变量声明 改变了, 这显然是咱们所不但愿的ip
那既然不想counter被其余因素所改变, 那咱们把counter做为累加器的内部变量不就好了么, 这样只有函数本身能用, 行吗? 显然不行内存
由于函数在执行完毕后, 会销毁其所在的上下文环境, 保存在AO中的变量会被垃圾回收机制释放, 对于没法一直保存在内存中的变量, 咱们显然没法一直对他进行操做
那有没有办法能够解决这个需求: 咱们既想把 counter 声明为局部变量, 又让他一直保留在内存中呢 ?
答案是能够的, 这就是咱们须要闭包的缘由
我的理解:
咱们知道: 执行上下文是一个运行时环境, 当函数执行完毕, 函数上下文会被释放, 局部变量天然也就没法一直保留
而闭包的存在, 就是帮助咱们把这些会被释放的上下文保存到内存中,使其一直存在
MDN中给出闭包的以下定义:
示例
function fn() { var count = 0 function addCount() { count++ } addCount() } fn()
上面有没有造成闭包呢? 咱们根据MDN的定义来分析一下:
addCount
是否是一个函数? 是addCount
对其周围状态有没有引用 ? 有, 引用了所处上下文环境中的 count
既然造成了闭包, 那能不能完成咱们上面的需求呢: **咱们既想把 counter 声明为局部变量, 又让他一直保留在内存中 **
咱们来接着调用addCount
函数, 看三次调用之后, count
的值是否是3
注意看 watch中count的变化
经过演示咱们发现, count
并无一直被保留在内存中, 而是每次调用都被初始化为了0
这显然没法完成咱们的需求
ECMAScript中, 给出以下定义:
从理论角度:全部的函数都是闭包。由于它们都在建立的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,由于函数中访问全局变量就至关因而在访问自由变量,这个时候使用最外层的做用域。
从实践角度:如下函数才算是闭包:
根据ECMAScript给出的定义, 咱们再来对比MDN给出的定义, 咱们发现, MDN中对闭包的定义其实就是ECMAScript中的闭包的理论角度,
但这对咱们的需求并无实际的做用, 那咱们再来看 从实际角度出发给出的定义
示例
function fn() { var count = 0 function addCount() { count++ } return addCount } var add = fn() add() add() add()
注意: 与上面代码不一样的是, 咱们此次将addCount函数做为返回值传递到外部
注意此时watch中count的变化
咱们能够清晰的看到, 内部变量count
, 在函数外部被一直引用, 并且实现了值的累加 !
也就是说, count
被一直保留在了内存之中, 这也真正实现了咱们上面所一直所提的需求:
其实说了这么多, 可能仍是比较抽象, 咱们先回顾如下闭包的理解:
咱们知道: 执行上下文是一个运行时环境, 当函数执行完毕, 函数上下文会被释放, 局部变量天然也就没法一直保留
而闭包的存在, 就是帮助咱们把这些会被释放的上下文保存到内存中,使其一直存在
咱们举个例子:
假设我家院子 (内存)里有一个大水缸 (不能释放的内存), 水缸里面有满满的水 (全局变量), 这个水过路口渴的人 (其余函数等)均可以取来喝,可是不一样的人都来喝, 不就把我家的水给 污染 了么, 何况这是我家, 大家怎么能想喝就喝呢, 因而我想出了这样一个办法:
我先把水(此时变成局部变量)都移到纸箱(外层函数)里, 可是纸箱存不住水 (函数被释放)呀, 因而我又立刻把这些水都灌进水瓶 (内层函数)里, 而后把这些水瓶放在纸箱里 (函数嵌套), 这样咱们的水得以保存, 可是别人也想喝怎么办, 那就按照个人说明去纸箱里拿水瓶 (暴露给外部的引用, 到此造成闭包), 而且你只能经过暴露出的水瓶来喝水
后来家家装了自来水, 你们也都不须要来我家喝水了, 因而我就把全部的水瓶丢掉(清除引用, 释放闭包的内存),
综上:
怎么理解闭包:
咱们知道: 执行上下文是一个运行时环境, 当函数执行完毕, 函数上下文会被释放, 局部变量天然也就没法一直保留
而闭包的存在, 就是帮助咱们把这些会被释放的上下文保存到内存中,使其一直存在
什么才算闭包:
从实践角度:如下函数才算是闭包:
1. 访问函数内部的变量, 也就是上面一直提的需求
function fn() { var count = 0 function addCount() { count++ } return addCount } var add = fn() add()
2. 定时器传值, 本质与上面一致
function func(param) { return function () { alert(param) } } var f1 = func(1) setTimeout(f1, 1000)
function isType(type) { return function (target) { return `[object ${type}]` === Object.prototype.toString.call(target) } } const isArray = isType('Array') console.log(isArray([1, 2, 3])); // true console.log(isArray({}));