这是我参与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 = 10、b = 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 (){} // 可以没有函数名
| 特征 | 函数声明 | 函数表达式 |
|---|---|---|
| 预提升 | 会进行预提升 | 不会进行预提升 |
| 访问范围 | 函数内部和函数父级作用域 | 函数内部 |
| 函数名 | 不能没有 | 可以没有 |