博客园首页新随笔联系订阅 管理 javascript中函数声明和函数表达式浅析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介:

记得在面试腾讯实习生的时候,面试官问了我这样一道问题。

1
2
3
//下述两种声明方式有什么不同
function  foo(){};
var  bar =  function  foo(){}; 

当初只知道两种声明方式一个是函数声明一个是函数表达式,具体有什么不同没能说得很好。最近正好看到这方面的书籍,就想好好总结一番。

在ECMAScript中,有两个最常用的创建函数对象的方法,即使用函数表达式或者使用函数声明。对此,ECMAScript规范明确了一点,即是,即函数声明 必须始终带有一个标识符(Identifier),也就是我们所说的函数名,而函数表达式则可以省略。

函数声明:

function Identifier ( FormalParameterList opt){ FunctionBody }

函数声明解析过程如下:

  • 创建一个new Function对象,FormalParameterList指定参数,FunctionBody指定函数体。将当前正在运行环境中作用域链作为它的作用域。

  • 为当前变量对象创建一个名为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(){} 被包含在一个函数体内,或者位于程序(的最上层)中,则将它作为一个函数声明来解析。显然,在省略标识符的情况下,“表达式” 也就只能是表达式了。

1
2
3
4
5
6
7
8
9
function  foo(){};  // 声明,因为它是程序的一部分
 
var  bar =  function  foo(){};  // 表达式,因为它是赋值表达(AssignmentExpression)的一部分
 
new  function  bar(){};  // 表达式,因为它是New表达式(NewExpression)的一部分
 
( function (){
     function  bar(){};  // 声明,因为它是函数体(FunctionBody)的一部分
})();

还有一种情况:

1
( function  foo(){})

这种情况也是函数表达式,它被包含在一对圆括号中的函数,在其上下文环境中,()构成了一个分组操作符,而分组操作符只能包含表达式,更多的例子:

1
2
3
4
5
6
7
8
9
10
function  foo(){};  // 函数声明
 
( function  foo(){});  // 函数表达式:注意它被包含在分组操作符中
 
try  {
( var  x = 5);  // 分组操作符只能包含表达式,不能包含语句(这里的var就是语句)
}
catch (err) {
// SyntaxError(因为“var x = 5”是一个语句,而不是表达式——对表达式求值必须返回值,但对语句求值则未必返回值。——译
}

 

   函数声明与函数表达式的异同

下面简单说说。声明和表达式的行为存在着十分微妙而又十分重要的差别。

首先,函数声明会在任何表达式被解析和求值之前先行被解析和求值。即使声明位于源代码中的最后一行,它也会先于同一作用域中位于最前面的表达式被求值。还是看个例子更容易理解。在下面这个例子中,函数 fn 是在 alert 后面声明的。但是,在alert 执行的时候,fn已经有定义了:

1
2
3
4
5
alert(fn());  //输出Helloworld!   
 
function  fn() {
return  'Helloworld!' ;
}

简单总结,区别在什么地方呢?

  • 声明总是在作用域开始时先行解析;

  • 表达式在遇到时候才运算。

函数声明还有另外一个重要的特点,即通过条件语句控制函数声明的行为并未标准化,因此不同环境下可能会得到不同的结果。即是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 千万不要这样做!
// 不同浏览器会有不同返回结果,
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()都是通过函数声明模式声明:

1
2
3
4
5
6
7
8
// 全局环境
function  foo() {} 
 
function  local() { 
// 局部环境 
     function  bar() {} 
         return  bar; 
}

当你在语法上不能使用函数声明的时候,你就可以使用函数表达式。比如:传递一个函数作为参数或者在对象字面量中定义一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 这是一个匿名函数表达式
callMe( function () {
 
     //传递一个函数作为参数
});
 
// 这是一个具名函数表达式
callMe( function  me() {
 
     // 传递一个函数作为参数,函数名为me
});
 
// 其他函数表达式
var  myobject = {
     say:  function () {
 
         // I am a function expression 
     }
};

学识有限,如有错误,欢迎指正。

 


本文转自ChokCoco博客园博客,原文链接:http://www.cnblogs.com/coco1s/p/3959927.html

目录
相关文章
|
1天前
|
JavaScript 前端开发 Java
[JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂
本文介绍了JavaScript中常用的函数和方法,包括通用函数、Global对象函数以及数组相关函数。详细列出了每个函数的参数、返回值及使用说明,并提供了示例代码。文章强调了函数的学习应结合源码和实践,适合JavaScript初学者和进阶开发者参考。
10 2
[JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂
|
14天前
|
JavaScript 前端开发
JavaScript 函数语法
JavaScript 函数是使用 `function` 关键词定义的代码块,可在调用时执行特定任务。函数可以无参或带参,参数用于传递值并在函数内部使用。函数调用可在事件触发时进行,如用户点击按钮。JavaScript 对大小写敏感,函数名和关键词必须严格匹配。示例中展示了如何通过不同参数调用函数以生成不同的输出。
|
16天前
|
存储 JavaScript 前端开发
JS函数提升 变量提升
【10月更文挑战第6天】函数提升和变量提升是 JavaScript 语言的重要特性,但它们也可能带来一些困惑和潜在的问题。通过深入理解和掌握它们的原理和表现,开发者可以更好地编写和维护 JavaScript 代码,避免因不了解这些机制而导致的错误和不一致。同时,不断提高对执行上下文等相关概念的认识,将有助于提升对 JavaScript 语言的整体理解和运用能力。
|
25天前
|
JavaScript 前端开发
js教程——函数
js教程——函数
21 4
|
2月前
|
JavaScript 前端开发 安全
JavaScript函数详解
JavaScript函数的详细解析,包括函数的定义和调用方式(如一般格式、匿名函数、构造函数、自调用函数、箭头函数和严格模式)、函数参数(arguments对象、可变参数、默认参数值)、闭包的概念和应用实例。
JavaScript函数详解
|
24天前
|
存储 JavaScript 前端开发
js中函数、方法、对象的区别
js中函数、方法、对象的区别
15 2
|
27天前
|
JavaScript 前端开发 Java
【javaScript数组,函数】的基础知识点
【javaScript数组,函数】的基础知识点
22 5
|
26天前
|
JavaScript 前端开发
Node.js 函数
10月更文挑战第5天
19 3
|
30天前
|
前端开发 JavaScript
探索JavaScript函数基础
探索JavaScript函数基础
16 3
|
1月前
|
JavaScript 前端开发
在JS中如何声明一个函数
在JS中如何声明一个函数
24 2