函数声明和函数表达式

1,118 阅读4分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

前言

函数式编程是前端开发主流的编程模式,但是一直使用函数的你,知道函数的定义两种方式吗?

本文主要讲解函数表达式是什么、函数声明是什么、函数表达式函数声明的异同点、自执行函数函数表达式的联动。

引言

我们在封装模块的时候经常会采用自执行函数(IIFE) ,它完整的英语是Immediately Invoked Function Expression,翻译成中文就是立即调用函数表达式,这里可以由文字反应两个特点:

  • 立即调用
  • 函数表达式
 //自执行函数
 (function test1(){
    console.log("自执行函数") 
 })()
 //函数声明
 function test2(){
     console.log("函数声明") 
 }
 test2()

我们对比自执行函数和函数声明,会发现多了两对括号。末尾的那一对很好理解,就是调用函数,这里就很好理解特点①立即调用,就是函数在定义的时候在最后加上()立即去调用函数。但是特点②函数表达式就很不好理解了,什么是函数表达式?他和另外一对括号有什么联系?带着这些疑问进入函数的学习吧。

什么是表达式和语句

 let a = 10;
 let b;
 b = a + 1;

上面的代码中let a = 10;let b;就可以称为语句,而a = 10b = a + 1就是表达式;

可以理解为:语句是一个行为;表达式会产生值

表达式语句

function test(){};   // 函数声明语句
{
    a: {
        if (!window) break a;
    }
};                 //  代码块语句

可以看到上面代码中的语,这种表达式作为语句的被称为表达式语句,虽然单独为语句,但是在也可以像表达式一样去返回值

function test(){};     // 本意是想把语句当做 “函数声明语句”
function test(){}();   // 本意是想把语句当做 “函数表达式语句”,然后立即执行
​
{
    a: {}
};              // 本意是想把语句当做 “赋值语句”
{ a: {} }     // 本意是想把语句当做 “对象字面量表达式语句”

执行上面的代码,其实会发现除了函数声明语句赋值语句才能正确执行,也就是说表达式作为语句都不能被合理编译成功!!!

其实通过上面的例子不难发现,相同的代码可以了表达不同语义,让编译器也不知道到底是语句还是表达式语句,JavaScript为了避免歧义带来的错误和误解

JavaScript语法禁止表达式语句以大括号或关键字"function"开头

  • 可以选择用括号包括表达式语句
function test(){};   // 函数声明语句
(function test(){});   // 函数表达式语句
​
{
    a: {}    //这样是形成一个块级作用域,快级作用域语句
}; 
({ a: {} });    // 对象字面量表达式语句
  • 也可以使用(+=等运算符开头,来定义表达式语句
+ function test(){}; // 函数表达式语句
+ { a: {} }          // 对象字面量表达式语句

函数声明

先回顾一下函数声明的使用

 test2()
 //函数声明
 function test2(){
     console.log("函数声明") 
 }
 test2()

需要先声明函数,在使用的地方调用这个函数

特点

  • 函数需要声明,才可以调用,并且声明和调用在代码中的位置没有硬性规则,声明可以在调用前,也可以在调用后面。
  • 函数声明会预编译(预提升)
  • 函数在执行时会形成一个函数作用域
  • 必须要有函数名称

函数表达式

回调函数、自执行函数、闭包向外暴露函数一般都会使用函数表达式

 //回调函数
 setTimeout(function(){},1000) 
 //自执行函数
 (function test1(){
    console.log("自执行函数") 
 })()

对比函数声明可以发现函数表达式的特点是:函数前面除了 function在前面还包含其他符号(,或者其他符号

这里也可以从语句和表达式的层次理解:函数声明是语句,用于实现某个行为;而函数表达式是表达式,用于产生值

函数表达式和函数声明的异同点

因为函数表达式和函数声明属于函数的分支,所以对于函数的基本特征都一样。比如:创建函数作用域、可以传递参数、有返回值等。

等然之前也有独特特征,一个是声明语句,一个表达式。

预提升

函数声明会进行预提升,所以在函数内部和函数父级作用域都是可以调用

函数表达式不会预提升,并且除了函数内部其他地方都不能通过名称调用函数

 test()
 function test(){}  //预提升
 cs()   //报错
 let cs=function test(){}
 cs()    //正常使用
 test()  //不能访问,所以报错

函数名

 function test(){}   //不能没有函数名
 let cs=function (){} // 可以没有函数名
特征函数声明函数表达式
预提升会进行预提升不会进行预提升
访问范围函数内部和函数父级作用域函数内部
函数名不能没有可以没有