JavaScript 闭包

122 阅读2分钟

作用域

  • software security:least exposure

    • naming collision

    • unexpected behavior

    • unintended dependency

闭包

  • 闭包跟创建的实例相关

  • 保存的是变量,可以修改

  • 对变量的引用

要素

  • 要返回出来一个引用(function)

  • 内层作用域要引用外层作用域

  • 在其他作用域调用

GC

  • 最后一个对这个函数的引用消失,闭包就会被垃圾回收

  • 效率、性能

在一个函数中访问另一个函数作用域中变量的方法

闭包的用处

  • 函数外部可以访问到函数内部的变量

  • 跨作用域,创建私有变量

闭包经典题目结果和改造方式?*

for (var i = 1; i < 9; i++) {
  setTimeout(
    (function a() {
      console.log(i)
    },
    i * 1000)
  )
}

// 利用闭包解决 qaq
for (var i = 1; i < 9; i++) {
  (function (j) {
    setTimeout(
      (function a() {
        console.log(j)
      },
      j * 1000)
    )
  })(i)
}

// 利用作用域
for (let i = 1; i < 9; i++) {
  setTimeout(
    (function a() {
      console.log(i)
    },
    i * 1000)
  )
}

闭包作用域

var test = (function(i){
    return function(){
        alert(i*=2);
    }
})(2);

test(5);

// alert 输出的结果都会 toString()
// 立即执行函数
// 没有被占用就会销毁

image.png

var a=0,
    b=0;
function A(a){
    A=function(b){
        alert(a+b++);
    };
    alert(a++);
}

A(1);
A(2);
// GO:全局对象
// i++ i不变再++ i再变
// ++i i先变再++

image.png blog.csdn.net/weixin_4358…

使用场景

  • ajax

  • events

setTimeout

  • 原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果

    function f1(a) {
        function f2() {
            console.log(a);
        }
        return f2;
    }
    var fun = f1(1);
    setTimeout(fun,1000);//一秒之后打印出1
    
    12345678
    

回调

  • 定义行为,然后把它关联到某个用户事件上(点击或者按键)。代码通常会作为一个回调事件触发绑定到事件

  • 当点击数字时,字体也会变成相应的大小

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>测试</title>
    </head>
    <body>
        < a href=" " id="size-12">12</ a>
        < a href="#" id="size-20">20</ a>
        < a href="#" id="size-30">30</ a>
    
        <script type="text/javascript">
    
            function changeSize(size){
                return function(){
                    document.body.style.fontSize = size + 'px';
                };
            }
    
            var size12 = changeSize(12);
            var size14 = changeSize(20);
            var size16 = changeSize(30);
    
            document.getElementById('size-12').onclick = size12;
            document.getElementById('size-20').onclick = size14;
            document.getElementById('size-30').onclick = size16;
    
        </script>
    </body>
    </html>
    

函数防抖

优化 防抖 节流 - 掘金 (juejin.cn)

封装私有变量

  • 用js创建一个计数器

    • 方法1:在返回的对象中,用闭包携带了局部变量x,从外部代码根本无法访问到变量x

      function f1() {
        var sum = 0
        var obj = {
          inc: function () {
            sum++
            return sum
          },
        }
        return obj
      }
      let result = f1()
      console.log(result.inc()) //1
      console.log(result.inc()) //2
      console.log(result.inc()) //3
      
    • 方法2 qaq

      • 所有js数据类型都拥有valueOf和toString这两个方法,null除外

      • valueOf()方法:返回指定对象的原始值

      • toString()方法:返回对象的字符串表示

      • 在数值运算中,优先调用了valueOf

      • 字符串运算中,优先调用toString

      • sum+'' 是一个字符串类型的数据

      • i++是先赋值,然后再自增;++i是先自增,后赋值

      function f1() {
        var sum = 0
        function f2() {
          sum++
          return f2
        }
        f2.valueOf = function () {
          return sum
        }
        f2.toString = function () {
          return sum + ''
        }
        return f2
      }
      //执行函数f1,返回的是函数f2
      console.log(+f1()) //0
      console.log(+f1()()) //1
      console.log(+f1()()()) //2