JavaScript中的函数声明和函数表达式区别浅析
作者:junjie 发布时间:2023-08-05 23:22:36
记得在面试腾讯实习生的时候,面试官问了我这样一道问题。
//下述两种声明方式有什么不同
function foo(){};
var bar = function foo(){};
当初只知道两种声明方式一个是函数声明一个是函数表达式,具体有什么不同没能说得很好。最近正好看到这方面的书籍,就想好好总结一番。
在ECMAScript中,有两个最常用的创建函数对象的方法,即使用函数表达式或者使用函数声明。对此,ECMAScript规范明确了一点,即是,即函数声明 必须始终带有一个标识符(Identifier),也就是我们所说的函数名,而函数表达式则可以省略。
函数声明:
function Identifier ( FormalParameterList opt){ FunctionBody }
函数声明解析过程如下:
1. 创建一个new Function对象,FormalParameterList指定参数,FunctionBody指定函数体。将当前正在运行环境中作用域链作为它的作用域。
2. 为当前变量对象创建一个名为Identifier的属性,值为Result(1)。
函数表达式:
(函数表达式分为匿名和具名函数表达式)
function Identifier opt( FormalParameterList opt){ FunctionBody } //这里是具名函数表达式
具名函数表达式的解析过程如下:
1. 创建一个new Object对象
2. 将Result(1)添加到作用域链的顶端
3. 创建一个new Function对象,FormalParameterList指定参数,FunctionBody指定函数体。将当前正在运行的执行环境中作用域链作为它的作用域。
4. 为Result(1)创建一个名为Identifier 的属性,其值为为Result(3),只读,不可删除
5. 从作用域链中移除Result(1)
6. 返回Result(3)
官方文档读起来十分拗口。简单来说,ECMAScript是通过上下文来区分这两者的:假如 function foo(){} 是一个赋值表达式的一部分,则认为它是一个函数表达式。而如果 function foo(){} 被包含在一个函数体内,或者位于程序(的最上层)中,则将它作为一个函数声明来解析。显然,在省略标识符的情况下,“表达式” 也就只能是表达式了。
function foo(){}; // 声明,因为它是程序的一部分
var bar = function foo(){}; // 表达式,因为它是赋值表达(AssignmentExpression)的一部分
new function bar(){}; // 表达式,因为它是New表达式(NewExpression)的一部分
(function(){
function bar(){}; // 声明,因为它是函数体(FunctionBody)的一部分
})();
还有一种情况:
(function foo(){})
这种情况也是函数表达式,它被包含在一对圆括号中的函数,在其上下文环境中,()构成了一个分组操作符,而分组操作符只能包含表达式,更多的例子:
function foo(){}; // 函数声明
(function foo(){}); // 函数表达式:注意它被包含在分组操作符中
try {
(var x = 5); // 分组操作符只能包含表达式,不能包含语句(这里的var就是语句)
}
catch(err) {
// SyntaxError(因为“var x = 5”是一个语句,而不是表达式——对表达式求值必须返回值,但对语句求值则未必返回值。——译
}
下面简单说说函数声明与函数表达式的异同。声明和表达式的行为存在着十分微妙而又十分重要的差别。
首先,函数声明会在任何表达式被解析和求值之前先行被解析和求值。即使声明位于源代码中的最后一行,它也会先于同一作用域中位于最前面的表达式被求值。还是看个例子更容易理解。在下面这个例子中,函数 fn 是在 alert 后面声明的。但是,在alert 执行的时候,fn已经有定义了:
alert(fn()); //输出Helloworld!
function fn() {
return 'Helloworld!';
}
简单总结,区别在什么地方呢?
1. 声明总是在作用域开始时先行解析;
2. 表达式在遇到时候才运算。
函数声明还有另外一个重要的特点,即通过条件语句控制函数声明的行为并未标准化,因此不同环境下可能会得到不同的结果。即是:
// 千万不要这样做!
// 不同浏览器会有不同返回结果,
if (true) {
function foo() {
return 'first';
}
}
else {
function foo() {
return 'second';
}
}
foo();
// 记住,这种情况下要使用函数表达式:
var foo;
if (true) {
foo = function() {
return 'first';
};
}
else {
foo = function() {
return 'second';
};
}
foo();
那么,使用函数声明的实际规则到底是什么?
FunctionDeclaration(函数声明)只能出现在Program(程序)或FunctionBody(函数体)内。从句法上讲,它们 不能出现在Block(块)({ ... })中,例如不能出现在 if、while 或 for 语句中。因为 Block(块) 中只能包含Statement(语句), 而不能包含FunctionDeclaration(函数声明)这样的SourceElement(源元素)。
另一方面,仔细看一看产生规则也会发现,唯一可能让Expression(表达式)出现在Block(块)中情形,就是让它作为ExpressionStatement(表达式语句)的一部分。但是,规范明确规定了ExpressionStatement(表达式语句)不能以关键字function开头。而这实际上就是说,FunctionExpression(函数表达式)同样也不能出现在Statement(语句)或Block(块)中(别忘了Block(块)就是由Statement(语句)构成的)。
由于存在上述限制,只要函数出现在块中(像上面例子中那样),实际上就应该将其看作一个语法错误,而不是什么函数声明或表达式。
那么我们应该在什么时候使用函数声明或函数表达式呢?函数声明只能出现在“程序代码”中,意味着只能在其它函数体中或者全局空间;它们的定义不能不能赋值给一个变量或属性,或者作为一个参数传递出现在函数调用中;下面的例子是函数声明的允许的用法,foo(),bar()和local()都是通过函数声明模式声明:
// 全局环境
function foo() {}
function local() {
// 局部环境
function bar() {}
return bar;
}
当你在语法上不能使用函数声明的时候,你就可以使用函数表达式。比如:传递一个函数作为参数或者在对象字面量中定义一个函数:
// 这是一个匿名函数表达式
callMe(function () {
//传递一个函数作为参数
});
// 这是一个具名函数表达式
callMe(function me() {
// 传递一个函数作为参数,函数名为me
});
// 其他函数表达式
var myobject = {
say: function () {
// I am a function expression
}
};
学识有限,如有错误,欢迎指正。


猜你喜欢
- 斐波那契数列(Fibonacci)最早由印度数学家Gopala提出,而第一个真正研究斐波那契数列的是意大利数学家 Leonardo Fibo
- 程序运行效率程序的运行效率分为两种:第一种是时间效率,第二种是空间效率。时间效率被称为时间复杂度,而空间效率被称作空间复杂度。时间复杂度主要
- 这里我们通过请求网页例子来一步步理解爬虫性能当我们有一个列表存放了一些url需要我们获取相关数据,我们首先想到的是循环简单的循环串行这一种方
- 想要查看每次训练模型后的 loss 值变化需要如下操作loss_value= [ ]self.history = model.fit(sta
- Django的Form主要具有一下几大功能:生成HTML标签验证用户数据(显示错误信息)HTML Form提交保留上次提交数据初始化页面显示
- 在项目中,尤其是pc端的时候,我们在用户登录后会给前端返回一个标识,来判断用户是否登录,这个标识大多数都是用户的id  
- 今天主要记录一下pandas去重复行以及如何分类汇总。以下面的数据帧作为一个例子: import pandas as pddata
- 说下思路吧:原图->灰度->根据像素亮度-映射到指定的字符序列中->输出。字符越多,字符变化稠密。效果会更好。如果根据灰度
- Sql Server 解决“用户登录失败,错误编号18456”1.说明因密码或用户名错误而使身份验证
- 本文实例讲述了Python排序搜索基本算法之冒泡排序。分享给大家供大家参考,具体如下:冒泡排序和选择排序类似,也是第n次把最小的元素排在第n
- 一、图像直方图1.1 定义图像直方图是图像的基本属性之一,也是反映图像像素数据分布的统计学特征,其横坐标代表了图像像素点在[0,255]范围
- 前言有时候python自带异常不够用,如同java,python也可以自定义异常,并且可以手动抛出。注意,自定义异常只能由自己抛出。pyth
- SQLyog是一款MySQL可视化工具,他可以将部分SQL操作通过图形化界面操作来完成,方便开发者更好的进行开发及数据库设计。在安装SQLy
- 简介pycurl类似于Python的urllib,但是pycurl是对libcurl的封装,速度更快。本文使用的是pycurl 7.43.0
- 关于Django生成迁移文件,我是在虚拟机上完成的1.创建虚拟环境:在终端上输入创建python3的虚拟环境mkvirtualenv -p
- 问题背景查询MySQL中用逗号分隔的字段【a,b,c】是否包含【a】场景模拟现有表【ec_logicplace】,如下图所示:要求判断数值【
- 已知有一个XML文件(bookstore.xml)如下:<?xml version="1.0" encoding=
- 我 们知道Express是一个基于NodeJS的非常优秀的服务端开发框架,本篇CSSer将提供express框架的route和route c
- 这个目前还是有个别无法显示,翻了下msdn貌似没看到更好的解决方案,暂时放弃继续研究,有晓得完全解决的朋友不妨回复说一声。 先附bat创建畸
- 软硬件环境OS X EI CapitanPython 3.5.1mysql 5.6前言在开发中经常涉及到数据库的使用,而python对于数据