JS高级(4)函数高级 — 闭包

本文详细介绍了JavaScript中的闭包概念,包括如何产生闭包、闭包的本质、产生闭包的条件、常见闭包场景及作用。闭包能够延长局部变量的生命周期,允许外部访问内部数据,但可能导致内存占用增加和内存泄漏。文章还讨论了闭包在定义JS模块中的应用,并提醒开发者谨慎使用闭包以避免潜在问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

🌟 闭包

//循环遍历,给每个按钮都绑定上单击响应函数
var btns = document.getElementsByTagName("button");
for(var i = 0; i< btns.length; i++){ 
//这样写会有一个问题,btns是一个伪数组,其中并不包含length属性,所以每次循环都是在单独计算长度
};
//可以修改为:
for(var i = 0; length= btns.length;  i<length; i++){
//提前提取出length的数值,以免重复计算
    var btn = btns[i];
//将btn对应的索引保存在对应的btn上
    btn.index = i;
    btn.onclick = function(){
    alert("这是第"+ (this.index+1)+"个");
    }
}; //函数在for循环执行之后才单击执行,所以只会显示for循环执行后的i的数值

//也可以利用闭包
for(var i= 0 length = btn.length; i<length; i++){
    (function(i){
        var btn = btn[i];
        btn.onclick = function(){
            alert("这是第"+ (i+1)+"个");
            };
     })(i); //匿名函数立即调用
};

1.如何产生闭包?

当一个嵌套的内部函数引用了嵌套的外部函数的变量时,就产生了闭包。

function fn1(){
    var a = 2;
    function fn2(){
    console.log(a);
        };
};
fn1();
//fn2引用了fn1的变量,所以产生了闭包

2. 闭包到底是什么?

闭包是嵌套的内部函数,即fn2(多数人的理解);闭包包含被引用变量的对象,即fn2中的closure。(少数人的理解)

闭包存在于嵌套的内部函数中。

3. 产生闭包的条件?

函数嵌套+内部函数引用了外部函数的数据(变量/函数)*外部函数要调用才可以产生

function fn1(){
    var a = 2;
    var b = "abc"; //闭包中不回含有b变量,因为fn2不需要b
    function fn2(){ //执行内部函数定义就会产生闭包,不需要调用内部函数,但是一定要调用外部函数
                    //外部函数不调用无法定义内部函数,一旦调用就会预处理内部函数
        console.log(a);
        };
    fn2();
};
fn1();

4. 常见的闭包

(1)将函数作为另一个函数的返回值

function fn1(){
    var a = 1;
    function fn2 (){
        a++;
        console.log(a);
    };
    return fn2;
}; 
var f = fn1(); //闭包产生
f();//2
f();//3
//a是局部变量,本来应该在执行fn1的时候产生,在调用结束后就死亡。
//外部函数执行几次,就产生了几个闭包。
//执行外部函数的时候才会去创建内部函数对象,跟内部函数执行几次无关。

(2)将函数作为参数传递给另一个函数调用

function showDelay(msg, time){
    setTimeout(function(){
        alert(msg);    
    }, time)
};
showDelay("lele", 2000);

//有嵌套函数,内部函数引用外部变量msg,并且调用了外部函数,闭包产生

5. 闭包的作用

 (1)使函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)

 (2)让函数外部可以操作(读写)到函数内部的数据(变量/函数)

function fn1 (){
     var a = 1;
     function fn2(){
         a++;
         console.log(a);
};
     function fn3(){
        a--;
        console.log(a);
};
     return fn3;
};

var f = fn1();
f(); //0

* 闭包在函数fn1调用时产生,里面只有变量a;当函数执行完毕后,fn3这个变量消失,但是fn3指向的对象并不会消失,因为f保存了该函数的地址值并指向这个对象,只要有变量指向对象,这个对象就不是垃圾对象。

问题:1⃣️ 在函数执行完成后,函数内部声明的局部变量是否还存在?

                  一般是不存在的,存在于闭包中的变量才会存在。

            2⃣️ 在函数外部能够直接访问函数内部的局部变量吗?不能,但是通过闭包可以

6. 闭包的生命周期

产生:在嵌套内部函数定义执行(函数对象产生,未调用)完成时就产生

死亡:在嵌套的内部函数成为垃圾对象时

f = null;//闭包死亡,包含闭包的函数对象成为垃圾对象
function fn1(){
    var a = 2;
    var fn2 = function(){ //使用变量赋值的方法创建函数则会在这行执行,而不是内部函数定义执行时产生闭包
        console.log(a); 
    };
};

7. 闭包应用 — 定义JS模块

JS模块:具有特定分功能的js文件,将所有的数据和功能都封装在一个函数内部(私有的),只向外暴露一个包含n个方法的对象或函数。

function myModule(){
//私有数据
    var msg = "meow meow";
//操作数据的函数
    function fn1 (){
        console.log("fn1"+msg.toUpperCase());
    };
    function fn2(){
        console.log("fn2"+msg.toLowerCase());
    };
//向外暴露对象(给外部使用的方法)
//将两个函数封装在一个对象中,就可以同时暴露两个方法
    return {
        fn1:fn1;
        fn2:fn2;
    };
};
var module = myModule();
module.fn1();
(function(){
//私有数据
    var msg = "meow meow";
//操作数据的函数
    function fn1 (){
        console.log("fn1"+msg.toUpperCase());
    };
    function fn2(){
        console.log("fn2"+msg.toLowerCase());
    };
    window.myModule2 = {
    fn1:fn1;
    fn2:fn2;
    };
};)();//匿名函数自调用

8. 闭包的缺点

(1)函数执行完后,函数内部的局部变量没有被释放,占用内存时间会变长;

(2)容易造成内存泄漏,即由于疏忽或错误造成程序未能释放已经不再使用的内存。

function fn1(){
    var arr = New Array[10000]; //使用中括号表示有10000个空位
    function fn2(){
        console.log(arr.length);
    };
    return fn2;
};
var f = fn1();
f(); //f一直指向fn1,如果忘记写f=null; 则这块内存一直都被占用着
f = null; // 让内部函数成为垃圾对象---回收闭包

*能不用闭包就不用闭包/ 即使释放 

内存溢出:当程序运行需要的内存超过了剩余的内存时,就出现了内存溢出的错误。

内存泄漏:占用的内存没有及时释放。常见的内存泄漏:意外的全局变量;没有及时清理的计时器或回调函数;闭包。

//意外的全局变量
function fn(){
    a = 3; //以为是局部变量,其实是window.a全局变量;要使用var关键字
    console.log(a);
};
fn();

//启动循环定时器后不清理
var intervalid = setInterval(function(){
    console.log(":)");
},1000);
clearInterval(intervalid); //如果不清除则会一直占用内存

9. 面试题

var name = "The Window";
var object = {
    name:"My Object";
    getNameFunc: function(){
        return function(){
            return this.name;
        };
    };
};
alert(object.getNameFunc()()); //"The Window",没有闭包,对象不会形成作用域


var name2 = "The Window";
var object2 = {
    name2:"My Object";
    getNameFunc: function(){
        var that = this;
        return function(){
            return that.name2; //有闭包,内部函数引用外部函数的变量
        };
    };
};
alert(object2.getNameFunc()()); //"My Object"


function fun (n,o){
    console.log(o);
    return{
        fun:function(m){
            return fun(m,n);
        }
    };
};

var a = fun(0); 
a.fun(1);  
//这里产生了新的闭包,因为在执行fun(1,0)的时候外部函数又被调用了一次,但是因为没有变量接收调用的结果,所以闭包产生后又消失了
//如果是var b = a.fun(1); 后面再使用b调用fun(2),那么闭包内容是会变化的
a.fun(2);  
a.fun(3);   //undefined 0 0 0


var b = fun(0).fun(1).fun(2).fun(3);       // undefined 0 1 2
var c = fun(0).fun(1); c.fun(2);  c.fun(3);     //undefined 0 1 1

//直接调用内部函数不会产生闭包
//如果不产生新的闭包,闭包中的数据仍然是原来的数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值