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

//下述两种声明方式有什么不同
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(){} 被包含在一个函数体内,或者位于程序(的最上层)中,则将它作为一个函数声明来解析。显然,在省略标识符的情况下,“表达式” 也就只能是表达式了。

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!';
}

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

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

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

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

// 千万不要这样做!
// 不同浏览器会有不同返回结果,
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
}
};

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

javascript中函数声明和函数表达式浅析的更多相关文章

  1. 【JavaScript】Javascript中的函数声明和函数表达式

    Javascript有很多有趣的用法,在Google Code Search里能找到不少,举一个例子: <script> ~function() { alert("hello, ...

  2. javascript中函数声明与函数表达式的区别

    javascript中声明函数的方法有两种:函数声明式和函数表达式.究竟他们用起来有什么区别呢? 区别如下: (1).以函数声明的方法定义的函数,函数名是必须的,而函数表达式的函数名是可选的. (2) ...

  3. JavaScript 函数声明,函数表达式,匿名函数,立即执行函数之区别

    函数声明:function fnName () {-};使用function关键字声明一个函数,再指定一个函数名,叫函数声明. 函数表达式 var fnName = function () {-};使 ...

  4. 详解Javascript 函数声明和函数表达式的区别

    Javascript Function无处不在,而且功能强大!通过Javascript函数可以让JS具有面向对象的一些特征,实现封装.继承等,也可以让代码得到复用.但事物都有两面性,Javascrip ...

  5. javascript 函数声明与函数表达式的区别

    先看一段代码 var f = function g() { return 1; }; if (false) { f = function g(){ return 2; }; } alert(g()); ...

  6. [转]Javascript中的自执行函数表达式

    [转]Javascript中的自执行函数表达式 本文转载自:http://www.ghugo.com/javascript-auto-run-function/ 以下是正文: Posted on 20 ...

  7. Javascript函数声明与函数表达式

    在定义函数时,我们一般使用下面这两种方法: 使用函数声明定义: function sum (a, b) { return a + b; } 使用函数表达式定义: var sum = function ...

  8. javaScript的函数(Function)对象的声明(@包括函数声明和函数表达式)

    写作缘由: 平时再用js写函数的时候,一般都是以惯例 function fn () {} 的方式来声明一个函数,在阅读一些优秀插件的时候又不免见到 var fn = function () {} 这种 ...

  9. JavaScript的函数声明与函数表达式的区别

    1)函数声明(Function Declaration); // 函数声明 function funDeclaration(type){ return type==="Declaration ...

随机推荐

  1. src/main/Java路径下的properties文件丢失

    在pom中加入代码: <build> <resources> <resource> <directory>src/main/java</direc ...

  2. 纯CCS绘制三角形箭头图案

    用CSS绘制三角形箭头.使用纯CSS,你只需要很少的代码就可以创作出各种浏览器都兼容的三角形箭头! CSS代码: /* create an arrow that points up */ div.ar ...

  3. [WPF] 我的WPF自学日记1,无标题窗体拖动

    学习WPF的第一天,尝试写比较常用的功能,无标题窗体拖动. 先在设计界面给它加上MouseDown事件 <Window x:Class="MyFirstWPFAPP.MainWindo ...

  4. touchstart、touchmove、touchend 实现移动端上的触屏拖拽

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  5. SVN Server for Migration

    SVN Server: http://mxsuse01/svn/repos/erp/Oracle_EMS Report SVN (Put to SVN Sort) 1. *.RDF 2. *CP.LD ...

  6. SQL Server 求结果

    ;with cte as ( select  CONVERT(DATE, DATEADD(DAY, -9, GETDATE())) as paytime union all select datead ...

  7. ASP.NET TextBox 当鼠标点击后清空默认提示文字

    ASP.NET TextBox 当鼠标点击后清空默认提示文字 [ 方法一] 前台代码: <div>    <asp:TextBox ID="txtName" ru ...

  8. 学习笔记:因为java匿名类学习到接口的一些小用法

    在看CometD的示例代码时发现了许多有意思的代码,但说实话看别人的代码确实是件很累的事情,所以就看到这个知识点做一下记录吧.   先看一段代码: 代码1   这段代码中有一个new的操作,而且是在方 ...

  9. 在Visual Studio中将现有.NET Framework项目迁移至.NET Core 1.1 Preview 1

    1)下载安装包含 .NET Core 1.1 Preview 1 的 SDK:Windows x64 安装包(下载地址列表) 2)下载最新 VS 2015 NuGet 插件:https://dist. ...

  10. Java NIO5:选择器1---理论篇

    选择器 最后,我们探索一下选择器.由于选择器内容比较多,所以本篇先偏理论地讲一下,后一篇讲代码,文章也没有什么概括.总结的,写到哪儿算哪儿了,只求能将选择器写明白,并且将一些相对重要的内容加粗标红. ...