闭包

78 阅读2分钟

闭包

变量的作用链

  • JS变量有两种:全局变量、局部变量
  • 局部变量作用域一般在函数里,在函数外的视为全局变量
  • 在函数里面可以访问全局的变量,在函数外面不可以访问到函数里面的变量
  • JS存在“链式作用域”结构,可以理解为函数嵌套,子对象会一级一级的向上寻找父对象的变量。所以,父对象的所有变量对子对象都是可见的。
var str1="hello";//全局变量
function fun(){
    var res="i am coming";//fun()内的局部变量
}
function funa(){
    var str2=" world";//funa()内的局部变量
    function funb(){
        function func(){
            console.log(str1);//hello
            console.log(str2);//world
            console.log(res);//报错,undefined
        }
        func();
    } 
    funb();
}
funa();
console.log(str2);//报错
function fun1() {
            var a = 0;
            console.log(a);
            return function () {
                a++;
                console.log(a);
            }
        }
        //注意这种调用的结果:
        var res = fun1();
        res();
        res();
        res();
  • 第一次执行var res = fun1()把匿名函数的引用传给了变量res

  • 每次执行res()即在调用匿名函数

    • 一般来说局部变量在函数执行之后就会被垃圾回收机制回收,但是调用res()之后变量a没有被回收,每次执行一次res,a的值就会自增一次
    • res()函数可以记住并访问原来的词法作用域

什么是闭包

一个可以访问其他作用域的变量,并且能够记住所在的词法作用域的函数

怎么判断是一个闭包

当一个函数的返回值是另一个函数,这个返回的函数在原来所在的函数外部被执行了,并且访问了原来函数的变量,那这个上下文便产生了闭包环境。

闭包的优缺点

优点

  • 实现了可以访问其他作用域变量,并且避免了全局变量对自身词法作用域变量的污染
  • 可以把局部变量驻留在内存中一直保存着上一次执行的值,不会被垃圾回收机制回收,从而避免使用全局变量

缺点

  • 局部变量会一直驻留在内存中不会被回收,导致内存被爆满,影响程序性能

使用场景

比如想要缩放窗口,触发onresize事件,需要让他只触发一次

window.onresize=function(){
    console.log('只触发一次')
}

一般方法

window.onresize = function () {
            // console.log('onresize') //只想触发一次
            debounce(fn,1000)
        }
        var fn=function(){
            console.log('只触发了一次');
        }
        var time=null
​
        function debounce(fn,timeLong){
            if(time){
                clearTimeout(time)
                time=''
            }
            time=setTimeout(()=>{
                fn()
            },timeLong)
        }

二般方法

window.onresize=debounce(fn,1000)
​
        function debounce(fn){
            var timer=null
            return function(){
                if(timer){
                    clearTimeout(timer)
                    timer=null
                }
                timer=setTimeout(()=>{
                    fn()
                },1000)
            }
        }
​
        function fn(){
            console.log('二班只触发一次');
        }

老掉牙的方法

 for (var i = 1; i <= 10; i++) {
            ((j) => {
                setTimeout(() => {
                    console.log(j);
                })
            })(i)
        }