js原理:闭包

50 浏览发布于 作者 Yang (欢迎转载-请注明出处链接)留下评论分享按钮

一、什么是闭包:

一句话:子函数使用父函数局部变量 的情形,就是闭包。

//想让函数内的局部变量实现累加
//示例1,不使用闭包,没法实现累加
function Foo(){
     var i=0;
     return ++i;
}
var f1=Foo;
console.log(f1());  //1
console.log(f1());  //1
console.log(f1());  //1


//使用闭包,子函数访问父函数内的局部变量
function Foo(){  //父函数
     var i=0;
     return function(){  //子函数
         console.log(++i);  //子函数使用了父函数的局部变量 i
     }
}
var f1=Foo(); //Foo函数虽然return了,执行完了;但是局部变量 i 并没有被销毁
f1(); //1        //局部变量i在被累加
f1(); //2
f1(); //3

二、闭包的作用:

子函数使用的父函数的局部变量会被驻留在内存中而不会被垃圾回收机制销毁。

三、为何需要闭包:

当我们想要继续使用 函数内 局部变量时,就需要闭包。

函数代码的执行需要占用内存,一个函数执行内部只执行一次,执行完了返回,并从内存中清除该函数内申明的一切数据(变量),以释放内存。(函数一定会返回,要嘛无返回值的返回undefined,要嘛返回其它的,而返回后该函数执行时的那些上下文会被垃圾回收。)
负责释放内存的机制是 js的垃圾回收机制。当我们想要函数内的变量不被垃圾回收。就需要使用闭包。当垃圾回收机制要回收局部变量时,发现该变量还被子函数继续使用着,从而不会回收它。(如果回收了它,子函数就没法正确执行了)

四、闭包实现需注意:

  • 1、函数需要return出去  
  • 2、return出去的值是一个引用类型  
  • 3、使用return出去的函数时才会有驻留内存效果,光是使用函数fn1是没有闭包效果的  
  • 4、return 的那个函数就是闭包了,里面的this是指向window的,return出去的那个函数里面使用的变量是父函数的局部变量,如果要用全局的变量,可以加上this就可以:
//使用全局变量
var a = 9;
function fn1() {
    var a = 1;
    return function () {
        console.log(this.a);
        console.log(this.a++);
    }
}
fn1()();    //9 9   去掉a前面的this,就会打印1 1
fn1()();    //10 10   去掉a前面的this,就会打印1 1
fn1()();    //11 11   去掉a前面的this,就会打印1 1

五、闭包优缺点:

它的优点既是他的缺点,
优点:局部变量驻留内存,从而避免使用全局变量,避免了全局变量的污染问题。
缺点:因为闭包将局部变量驻留内存了,所以使用不当,容易造成 内存泄漏。


其实,更确切的说,闭包跟内存泄漏并不是直接因果关系,我们选择把要使用的变量放在闭包中,和放在全局中,对内存的影响是一致的。从这看并不能说成是内存泄漏。如果在将来不需要使用这些变量时,我们可以手动将这些变量设置null。让闭包跟内存泄漏有关系的,是我们使用的不当,比如我们使用闭包的同时,又有循环引用(在垃圾回收机制中,如果两个对象相互之间形成了循环引用,那么这两个对象都无法被回收)。循环引用造成的内存泄漏,这本身并非闭包的问题。

六、更多闭包示例:

function fn1() {
    var a = 1;
    function fn2() {
        alert(a);
        alert(a++);
    }
    return fn2; //返回出去的是一个引用类型的值
}
var f = fn1();
 f();    //返回1  返回1
 f();    //返回2  返回2
 f();    //返回3  返回3
var f = 1;
var f = fn1();
f()    //返回1  返回1,因为之前的f=1的赋值已经断开了f与fn1的连接了,然后fn1就被垃圾回收了


function Foo() {
    var i = 0;
    return function () {
        console.log(i++);
    }
}
var f1 = Foo(), f2 = Foo();    //给变量f1  f2赋值
f1(); //0
f1(); //1
f2(); //0
f1(); //2
//这里f1()函数和f2()函数在调用时会创建两个执行环境,保存各自的变量对象


function fn1() {
    var a = 1;
    function fn2() {
        alert(a);
        alert(a++);
    }
    return fn2; //返回出去的是一个引用类型的值
}
var f = fn1;       //注意此时fn1没有括号,所以f的引用地址只是函数fn1,不是它内部的return内容
f()();     //返回1  返回1
f()();    //返回1  返回1
f()();    //返回1  返回1

书上一个示例:(具体出自哪本书,当时做笔记没有备注上,无法注释引用了,后面知道了再补充书名)

想要打赏,请点击这里

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注