JavaScript中创建函数主要有两种方法:函数声明和函数表达式。这两种方式都有不同的适用场景。这篇笔记主要关注的是函数表达式的几大特点以及它的使用场景,下面一一描述。

  主要特点

  • 可选的函数名称

  函数名称是函数声明的必需组成部分,这个函数名称相当于一个变量,新定义的函数会复制给这个变量,以后函数的调用都需要通过这个变量进行。而对于函数表达式来说,函数的名称是可选的,例如下面的例子:

var sub = function(a1,a2){
return a1-a2;
}

  这个例子中函数表达式没有名称,属于匿名函数表达式。再看下面的例子:

var sub = function f(a1,a2){
return a1-a2;
}
console.log(f(5,3)); //错误调用方式
console.log(sub(5,3)); //正确调用方式

  在这个例子中,函数表达式的名称为f,这个名称f实际上变成了函数内部的一个局部变量,并且指代函数对象本身,在函数递归的时候有很大用处,后面会详细讲到。

  • 在执行阶段创建(区别于函数声明)

  这个特点是函数表达式明显区别于函数声明的地方。

  解释器在解析JavaScript代码时对于这两种方式并不是一视同仁的。解释器会首先读取函数声明,并使其在执行任何代码之前可用;而对于函数表达式,则必须等到解释器执行到它所在的代码行,才会被真正解析执行。例如:

console.log(add(1,2));   //"3"
console.log(sub(5,3)); //"unexpected identifier",报错
function add(a1,a2){
return a1+a2;
}
var sub = function(a1,a2){
return a1-a2;
}

  第一条语句完全可以正常执行。对代码求值时,JavaScript引擎在第一遍就会声明函数并通过一个名为函数声明提升的过程将它们放到源代码树的顶部。也就是说在执行环境的创建阶段(函数被调用但还没有开始执行)就会对函数声明进行"hosting"操作。所以,即使声明函数的代码在调用它的代码后面,JavaScript引擎也会把函数声明提升到顶部。但是如果把函数声明更改为函数表达式,就会在执行期间报错。原因在于在执行到函数所在的语句之前,变量sub中并不会包含对函数的引用。也就是说在代码执行阶段,变量sub才会被赋值。除了以上不同,在其它方面函数声明和函数表达式的语法是等价的。

  • 不会影响变量对象
var sub = function f(a1,a2){
console.log(typeof f); //"function"
return a1-a2;
}
console.log(typeof f); //"Uncaught ReferenceError: f is not defined(…)"

  通过上面的例子可以看到,函数名称f只能在函数对象内部使用,函数表达式的函数名称并不存在于变量对象中。

  使用场景

  函数表达式的使用场景很多。下面主要描述的是函数递归以及代码模块化方面的应用。

  • 函数递归

  看下面的例子:

function factorial(num){
if(num <= 1){
return 1;
}else{
return num * factorial(num - 1);
}
}

  这是一个经典的阶乘函数,但是这个例子存在的一个问题是函数名称factorial与函数体紧密耦合在一起,执行下面的语句就会报错:

var anotherFactorial = factorial;
factorial = null;
console.log(anotherFactorial(5)); //"Uncaught TypeError: factorial is not a function"

  报错的原因在于在函数体内部会调用factorial函数,而变量factorial对函数的引用已经被解除所以报错。这种情况的解决方法一般可以使用arguments.callee来解决,arguments.callee始终指向当前的函数,例如:

function factorial(num){
if(num <= 1){
return 1;
}else{
return num * arguments.callee(num - 1);
}
}

  这样在此执行anotherFactorial函数就可以得到正确结果了。但是在严格模式"strict"下,arguments.callee是不能通过脚本访问的,这是就可以使用函数表达式来解决这个问题了,例如:

var factorial = (function f(num){
if(num <= 1){
return 1;
}else{
return num * f(num - 1);
}
});
console.log(factorial(5)); //"120"
  • 代码模块化

  JavaScript中没有块级作用域,但我们可以使用函数表达式模块化JavaScript代码。模块化代码中可以封装不必让使用者知道的细节,只暴露给使用者相关接口,同时可以避免对全局环境的污染,例如:

var person = (function(){
var _name = "";
return{
getName:function(){
return _name;
},
setName:function(newname){
_name = newname;
}
};
}());
person.setName("John");
person.getName(); //"John"

  这个例子中创建了一个匿名函数表达式,这个函数表达式中包含了模块自身的私有变量和函数;这个函数表达式的执行结果返回一个对象,对象中包含了模块暴露给使用者的公共接口。代码模块化的具体形式还有很多,例如在一些常用的JavaScript库中通常都会使用类似下面例子的立即执行函数:

(function(){
var _name = "";
var root = this;
var person = {
getName: function(){
return _name;
},
setName: function(newname){
_name = newname;
}
};
root.person = person;
}.call(this));
person.setName("John");
person.getName(); //"John"

  这种方式直接将包含模块公共接口的对象作为全局对象的一个属性,这样在其它地方直接可以使用全局对象的这个属性来使用这个模块了。

  这篇笔记只包含了JavaScript中函数表达式的一部分内容,可能存在不准确或错误的地方,希望能够指出!

JavaScript 函数表达式的更多相关文章

  1. JavaScript函数表达式、闭包、模仿块级作用域、私有变量

    函数表达式是一种非常有用的技术,使用函数表达式可以无需对函数命名,从而实现动态编程.匿名函数,是一种强大的方式,一下总结了函数表达式的特点: 1.函数表达式不同于函数声明,函数声明要求有名字,但函数表 ...

  2. JavaScript函数表达式

    函数表达式的基本语法形式 var functionName = function(arguments){ //函数体 } 递归建议 我们通过例子来一步步说明,递归的最佳实现方式.下面是普通递归调用的例 ...

  3. JavaScript函数表达式与函数声明

    什么是函数? 函数是事件驱动或者被调用时执行的重复代码块. 作用域: 1. 全局作用域 2. 函数作用域(局部作用域) var i = 100; //全局作用域 function fun(){ var ...

  4. javascript-函数声明和函数表达式-call-apply

    1.函数声明与函数表达式 <script type="text/javascript"> //函数表达式,解析器在像执行环境中加载数据时,函数表达式是解析器执行到这段代 ...

  5. 深入理解javascript系列(4):立即调用的函数表达式

    本文来自汤姆大叔 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行. 在详细了解这个之前,我们来谈了解一下“自执行”这个叫法,本文对这个功能的叫法 ...

  6. 一步步学习javascript基础篇(6):函数表达式之【闭包】

    回顾前面介绍过的三种定义函数方式 1. function sum (num1, num2) { return num1 + num2; }  //函数声明语法定义 2. var sum = funct ...

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

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

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

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

  9. JavaScript中的函数表达式

    在JavaScript中,函数是个非常重要的对象,函数通常有三种表现形式:函数声明,函数表达式和函数构造器创建的函数. 本文中主要看看函数表达式及其相关的知识点. 函数表达式 首先,看看函数表达式的表 ...

随机推荐

  1. oracle常用函数及示例

    学习oracle也有一段时间了,发现oracle中的函数好多,对于做后台的程序猿来说,大把大把的时间还要学习很多其他的新东西,再把这些函数也都记住是不太现实的,所以总结了一下oracle中的一些常用函 ...

  2. exp/imp 与 expdp/impdp 区别

    在平常备库和数据库迁移的时候,当遇到大的数据库的时候在用exp的时候往往是需要好几个小时,耗费大量时间.oracle10g以后可以用expdp来导出数据库花费的时间要远小于exp花费的时间,而且文件也 ...

  3. BZOJ 1006 【HNOI2008】 神奇的国度

    题目链接:神奇的国度 一篇论文题--神奇的弦图,神奇的MCS-- 感觉我没有什么需要多说的,这里简单介绍一下MCS: 我们给每个点记录一个权值,从后往前依次确定完美消除序列中的点,每次选择权值最大的一 ...

  4. 简单的转盘抽奖——CSS动画优化

    前言 前两天去一家公司面试,被问到一些小游戏的东西.面试官提到了刷红包还有抽奖这些怎么实现,当时简单说了下思路,回来之后想想还是说的太轻描淡写了,干说不做就是耍流氓,所以就做了一个(Demo & ...

  5. WPF - 属性系统 - APaas(AttachedProperty as a service)

    是的,文章的题目看起来很牛,我承认. 附加属性是WPF中的一个非常重要的功能.例如在设置布局的过程中,软件开发人员就常常通过DockPanel的Dock附加属性来设置其各个子元素所处的布局位置.同样地 ...

  6. .NET开发邮件发送功能的全面教程(含邮件组件源码)

    今天,给大家分享的是如何在.NET平台中开发“邮件发送”功能.在网上搜的到的各种资料一般都介绍的比较简单,那今天我想比较细的整理介绍下: 1)         邮件基础理论知识 2)         ...

  7. Entity Framework 6 Recipes 2nd Edition(9-3)译->找出Web API中发生了什么变化

    9-3. 找出Web API中发生了什么变化 问题 想通过基于REST的Web API服务对数据库进行插入,删除和修改对象图,而不必为每个实体类编写单独的更新方法. 此外, 用EF6的Code Fri ...

  8. nodejs处理url工具

    url模块提供3个方法:parse,format,resolve 1.parse 要先引入url模块 >url.parse('http://www.cnblogs.com/cate/108703 ...

  9. 01.Web大前端时代之:HTML5+CSS3入门系列~初识HTML5

    Web大前端时代之:HTML5+CSS3入门系列:http://www.cnblogs.com/dunitian/p/5121725.html 文档申明 <!--文档类型申明,html代表是ht ...

  10. Redis数据结构详解之List(二)

    序言 思来想去感觉redis中的list没什么好写的,如果单写几个命令的操作过于乏味,所以本篇最后我会根据redis中list数据类型的特殊属性,同时对比成熟的消息队列产品rabbitmq,使用red ...