深入理解JavaScript闭包

作用域

  • 变量加var修饰即为局部变量,否则为全局变量
    function foo(){
    a=1
    var b=1;
    }
    foo();
    console.log(a);//1
    console.log(b);//ReferenceError: b is not defined

  • 和函数内部的作用域不同,语句块内的作用域相当于全局
    i=999;//全局变量
    function foo(){
    var i=233;//局部变量
    console.log(i);
    }
    foo();//233
    console.log(i);//999
    for(var i=0;i<10;i++){ //此处的i为全局变量i
    console.log(i);//1 2 3 4 5 6 7 8 9
    }
    console.log(i);//10

  • 函数内部可以读取到函数外部的变量,但是函数外部的变量不能读取到函数内部的变量
    var a=1;
    function foo(){
    a=2;
    var b=2;
    }
    foo();
    console.log(a);//2
    console.log(b);//ReferenceError: b is not defined

  • 函数内部的函数也可能读到函数内部的变量
    function foo(){
    var a=1;
    function bar(){
    console.log(a);
    }
    bar();
    }
    foo(); //1

  • 因此可以用函数内部的函数作为返回值来获取函数内部的值
    function foo(){
    var a=2;
    function bar(){
    console.log(a);
    }
    return bar;

    }
    var getResult=foo();
    getResult();
    

闭包

  • 闭包可以用于获取函数内的局部变量
    function foo(){
    var i=1;
    function getI(){
    return i;
    }
    return getI;
    }
    console.log(i);//undefined
    var GI=foo();
    var i=GI();
    console.log(i);//i
  • 闭包可以用于缓存变量
    function foo(){
    var i=1;
    function getI(){
    return ++i;
    }
    return getI;
    }
    var GI=foo();
    var i=GI();
    console.log(i);//2
    i=GI();
    console.log(i);//3
    //说明此处依然是调用的foo的缓存
    //因为getI赋值给了全局变量GI,getI又依赖于foo,所以GI存在时foo就不会被回收

总结

  • 闭包可看作保存状态的函数,而该状态对外部是不可见的
    function foo(){
    var i=1;
    return function(a){
    i+=a;
    return i;
    }
    }
    var add=foo();
    console.log(add(0));//1
    console.log(add(5));//6

  • 巧用that指向外部变量
    var foo={
    i:1,
    add:function(a){
    that=this;
    return (function(){
    return that.i+a;
    })()
    },
    getI:function(){
    return (function(){
    return this.i;
    })()
    }
    }
    var f=foo;
    console.log(f.add(0));
    console.log(f.getI());
    console.log(f.add(5));

  • 用闭包解决异步执行带来的问题
    //一道经典的面试题
    for(var i=0;i<3;i++){
    setTimeout(function(){
    alert(i)
    },2000)
    }
    //实际弹出的是3 3 3,而非0 1 2
    //因为setTimeout()中执行的函数会放置到另一个队列里面执行
    //此时i早已变为3,当alert再执行时,会往上一个作用域中找i
    //此时弹出的就是3 3 3

    //解决办法如下
    for(var i=0;i<3;i++){
        (function(e){
            setTimeout(function(){
                alert(i)
            },2000)
        })(e);
    }
    //构造闭包,使循环赋值给e(即i的引用)
    

参考

学习Javascript闭包(Closure)

你可能感兴趣的:(深入理解JavaScript闭包)