什么是闭包


函数即使脱离了它原本所在的词法作用域,也能够访问在词法作用定义的变量。因为函数被return出去了,没有执行一直持有函数的执行期上下文,然后就形成了闭包。闭包 = 执行环境 + 函数
image.png
这个函数 a 可以放在别的文件里面,然后得到的 b 函数,不管你在什么时候调用,都会正确打印「hello,闭包」,一般的 a 函数执行完,hello 变量就会被释放,但是在闭包里面,这个 hello 直到 b 函数执行之前,hello 都不会被释放。应用场景太多了,定时器、防抖按钮 什么的都需要用到闭包,下面我们看看应用场景

应用闭包的主要场合:

定时器

let方式.png
大家很容易就想到输出5个5

例一:函数每个一秒返回一个5

  1. for (var i = 0; i < 5; i++) {
  2. setTimeout(function () {
  3. console.log(i)
  4. }, 1000 * i)
  5. }

上面可以简写为如下:

  1. for (var i = 0; i < 5; i++) {
  2. var temp = function () {
  3. console.log(i)
  4. }
  5. setTimeout(temp , 1000 * i)
  6. }
  7. temp()

例二:每隔1秒返回一个undefine

  1. for (var i = 0; i < 5; i++) {
  2. setTimeout(function (i) {
  3. console.log(i)
  4. }, 1000 * i)
  5. }

上面可以简写为如下:

  1. for (var i = 0; i < 5; i++) {
  2. var temp= function (i) {
  3. console.log(i);
  4. }
  5. setTimeout(temp, i * 1000);
  6. }
  7. temp()

例三:结果是立即输出0,1,2,3,4,

  1. for (var i = 0; i < 5; i++) {
  2. setTimeout((function(i) {
  3. console.log(i);
  4. })(i), i * 1000);
  5. }

image.png

如何模拟定时器的方式输出:

如何输出01,2,3,4,呢?可以考虑立即执行函数或者let的块级作用于的方式解决
立即执行函数.png

输出5个5.png

将定事情放在for循环.png

设计私有的方法和变量。(团队合作可以封装代码互不影响)

任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量。私有变量包括函数的参数、局部变量和函数内定义的其他函数。
把有权访问私有变量的公有方法称为特权方法(privileged method)。

  1. function closure(){
  2. var age =22;
  3. var resetAge=function(){
  4. age =20
  5. }
  6. var setAge = function (newAge) {
  7. age = newAge
  8. }
  9. var getAge = function(){
  10. console.log(age)
  11. return age
  12. }
  13. var obj ={
  14. resetAge: resetAge,
  15. setAge: setAge,
  16. getAge: getAge
  17. }
  18. return obj
  19. }
  20. let privateData = closure();
  21. privateData.getAge();
  22. privateData.setAge(29)
  23. privateData.getAge();
  24. privateData.resetAge();
  25. privateData.getAge();

简化代码如下:

  1. function closure() {
  2. var age = 22;
  3. //特权方法
  4. return obj = {
  5. resetAge: function () {
  6. age = 20
  7. },
  8. setAge: function (newAge) {
  9. age = newAge
  10. },
  11. getAge: function () {
  12. console.log(age)
  13. return age
  14. }
  15. }
  16. }
  17. let privateData = closure();
  18. privateData.getAge();
  19. privateData.setAge(29)
  20. privateData.getAge();
  21. privateData.resetAge();
  22. privateData.getAge();

闭包缺点:内存泄漏

闭包会引用包含函数的整个变量对象,如果闭包的作用域链中保存着一个HTML元素,那么就意味着该元素无法被销毁。所以我们有必要在对这个元素操作完之后主动销毁。
闭包 - 图7

闭包中的this对象

闭包 - 图8
在上面这段代码中,obj.getName()()实际上是在全局作用域中调用了匿名函数,this指向了window。
这里要理解函数名与函数功能是分割开的,不要认为函数在哪里,其内部的this就指向哪里。
window才是匿名函数功能执行的环境。
如果想使this指向外部函数的执行环境,可以这样改写:
闭包 - 图9

https://juejin.im/post/5c12598851882565422292f8
https://segmentfault.com/q/1010000009490850
https://www.cnblogs.com/echolun/p/11481991.html