七、立即执行函数,逗号表达式,闭包深入

本文深入探讨JavaScript中的立即执行函数表达式(IIFE)、逗号表达式以及闭包的概念。讲解了IIFE的两种形式、作用及应用场景,强调其在减少内存占用上的价值。接着讨论了表达式与函数执行的关系,以及逗号运算符的用法。在闭包部分,解释了闭包如何结合IIFE解决变量作用域问题,并通过示例阐述了闭包的产生、作用域链的工作原理以及闭包在内存管理和模块化编程中的作用。

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

立即执行函数,逗号表达式,闭包深入

IIFE

IIFE(立即执行函数表达式) :英文Immediately-invoked function expression;

IIFE常见两种形式:

//1. W3C建议 World Wild Web Consortium
(function(){
  console.log(1)
}());

(function(实参){
  console.log(1)
}(形参));

//2.常见形式
(function(){
  console.log(2)
})();

还有一种可以使用的方式

var num = function(){
    return 1;
}();//能执行吗? √
console.log(num); //1 

为什么引入IIFE呢?

IIFE的特点:

  • 自动执行
  • 执行完后立即释放
  • 可以返回函数结果(可以有返回值被保存)

当我们使用一个函数,我们 只想要一次执行结果,以后再不使用 (主要原因)

若是全局中的函数,只要被声明就在GO中,一直存在,即使函数被执行结束,也仍然存在,浪费内存或加载过慢。

表达式

()括号包裹的任何东西(变量、函数、对象 …)都叫表达式。

问题来了:

//情况1
function test(){

}();//能执行吗? ×,语法错误

//情况2
var num = function(){
    return 1;
}();//能执行吗? √
console.log(num); //1 

//情况3
var num2 = function(){
    console.log(2);
}();//能执行吗? √ 2
console.log(num2); //undefined

情况三中,控制台输出2代表函数立即执行了,而打印num2为undefined,代表函数执行完毕后,被销毁了。

情况二中,函数执行结束后,把值返回出去保存到变量中,这正是IIFE普遍的用法

情况一中,函数声明无法立即执行

再看一种情况:

function test(a){
    
}(6);//报错吗? × 
//这个组合被js引擎解析成->
function test(a){
}
(6);//把这里的东西当做表达式。  一定是表达式,才可以立即执行

表达式才可以被立即执行,表达式的形式为:

  • ()括号包裹任何东西都叫表达式

  • +、-、!、||、&&、++… (数据+运算符+函数->表达式)

    //例如:
    function test(){
    
    }(); //报错
    0 == function(){
        console.log(2)
    }(); //2 false
    ++ function(){
        console.log(2)
    }(); //2可立即执行, 会有报错
    

总结:将函数声明转化为表达式后,就可以使用执行符号,立即执行该函数,并且该函数声明的函数名自动就会被忽略掉。

表达式被立即执行时,JS引擎会自动忽略函数名

逗号表达式

逗号运算符,返回逗号最后的数据。

console.log((1,2,5,78)) //78

闭包深入(与IIFE结合)

想要打印0-9:

function test (){
    arr = [];
    for(var i = 0; i < 10; i++){
        arr[i] = function(){
            document.write(i + ' ');
        }
    }

    return arr;
}

var arr = test();
for(j = 0; j < 10; j++) {
    arr[j]();
} // '10' '10' 一共十个10

为什么?

当内部函数返回到外部并保存时,一定会产生闭包现象,闭包会使原来的作用域链不释放,可以访问或更改变量。(回答闭包问题的流程)

  1. for循环的第一个语句var i = 0,相当于是在函数test中声明了一个变量i。(把变量所属的位置找对)
  2. 在函数执行前一刻test的作用域链从顶往下依次为函数test的AO,全局的GO。(函数的作用域链是什么情况)
  3. 函数test执行前一刻(预编译),function被定义;此时它的作用域链上从顶部依次往下分别是函数test的AO,全局的GO。(函数执行时,内部函数被定义,此时内部函数的作用域链是什么情况)
  4. 函数test执行完毕,与自己的AO的连线断开,此时test的AO中的i值为10;(函数执行结束,函数AO并未被销毁,说明变量的情况)
  5. 函数test执行完毕以后,arr[]数组中存储着10个函数,产生了闭包的现象,在自己的作用域链上连接着test的AO不释放;(具体说明内部函数的作用域链上有什么产生了闭包的现象)
  6. 当这些函数被执行时,它们的作用域链上从顶部依次往下,自己函数的AO,函数test的AO,全局的GO。变量i值从作用域链顶部依次往下找,自己的函数的AO中没有找到i,就去test的AO中去找,此时i的值为10,输出结果为10;(作用域链查找变量的顺序)

打印0-9的正确方法:

//错误写法中的for循环
for(var i = 0; i < 10; i++){
    arr[i] = function(){
        document.write(i + ' ');
    }
}
// 立即执行直接打印0-9
function test (){
    var arr = [];
    // 使arr中保存立即执行函数的结果
    for(var i = 0; i < 10; i++){
        arr[i] = (function(){
            document.write(i + ' ');
        }());
    }

    return arr;//IIFE中的函数立即执行了,这里其实都不用返回外部
}

var arr = test();

最最常用的做法:IIFE传参

//立即执行传参打印0-9
function test (){
    var arr = [];
    for(var i = 0; i < 10; i++){
        (function(j){
            // 使arr中保存函数,此函数定义时就随变量的变化而变化
            arr[j] = function(){
                document.write(j + ' ');
            }
        }(i));
    }

    return arr;
}

var arr = test();
for(j = 0; j < 10; j++) {
    arr[j]();//IIFE中传参,使arr中的函数就保存当前值,这里函数被调用
}
// 依靠普通函数传参
function test (){
    var arr = [];
    for(var i = 0; i < 10; i++){
        arr[i] = function(num){ //接收参数
            document.write(num + ' ');
        }
    }

    return arr;
}

var arr = test();
for(j = 0; j < 10; j++) {
    arr[j](j);//传参
}

总结2种使用IIFE避免闭包影响情况:

  • 内部函数使用IIFE:直接保存IIFE执行的结果,在外部访问就出结果,不需要使用()执行符号。

    arr[i] = (function(){
        document.write(i + ' ');
    }());
    
  • IIFE包裹内部函数并传参(zui最常用):使函数定义时依靠参数,定义时就保存值,在外部需要访问并执行

    (function(j){
        // 使arr中保存函数,此函数定义时就随变量的变化而变化
        arr[j] = function(){
            document.write(j + ' ');
        }
    }(i));
    

闭包高级

函数被定义时生成[[scope]]->生成scope chain -> scope chain中存着上级的环境。

函数被执行的前一刻(预编译过程),生成自己的AO,排到scope chain的最顶端。

函数执行完毕的两种情况:

  • 一般情况:函数执行完毕,函数的scope chain与自身AO连线中断,函数的AO也被销毁。
  • 闭包现象:函数执行完毕,函数的scope chain与自身AO连线中断,但函数的AO没有被销毁,在内部函数的scope chain上存在

再说一遍闭包的定义

内部函数被返回到外部并保存时,一定会产生闭包现象,使原来的作用域链不释放(只要程序整个不被销毁,原来的作用域链就永不释放),从而可以对函数内部变量进行访问和更改。

闭包用途
  • 封装插件
  • 模块化编程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值