老生常谈JavaScript 函数表达式


Posted in Javascript onSeptember 01, 2016

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 相关文章推荐
JavaScript toFixed() 方法
Apr 15 Javascript
手机开发必备技巧:javascript及CSS功能代码分享
May 25 Javascript
javascript实现的简单计时器
Jul 19 Javascript
浏览器检测JS代码(兼容目前各大主流浏览器)
Feb 21 Javascript
原生js实现节日时间倒计时功能
Jan 18 Javascript
angularjs中的$eval方法详解
Apr 24 Javascript
jQuery实现多张图片上传预览(不经过后端处理)
Apr 29 jQuery
基于Bootstrap框架菜鸟入门教程(推荐)
Sep 17 Javascript
微信小程序踩坑记录之解决tabBar.list[3].selectedIconPath大小超过40kb
Jul 04 Javascript
vue实例中data使用return包裹的方法
Aug 27 Javascript
微信小程序 setData 对 data数据影响问题
Apr 18 Javascript
js实现消灭星星(web简易版)
Mar 24 Javascript
Ubuntu系统下Angularjs开发环境安装
Sep 01 #Javascript
利用Angularjs和原生JS分别实现动态效果的输入框
Sep 01 #Javascript
knockoutjs动态加载外部的file作为component中的template数据源的实现方法
Sep 01 #Javascript
深入理解jQuery3.0的domManip函数
Sep 01 #Javascript
总结AngularJS开发者最常犯的十个错误
Aug 31 #Javascript
ES6记录异步函数的执行时间详解
Aug 31 #Javascript
基于angularjs实现图片放大镜效果
Aug 31 #Javascript
You might like
咖啡知识 咖啡养豆要养多久 排气又是什么
2021/03/06 新手入门
把PHP安装为Apache DSO
2006/10/09 PHP
PHP可变函数的使用详解
2013/06/14 PHP
PHP中file_exists()判断中文文件名无效的解决方法
2014/11/12 PHP
php实现的递归提成方案实例
2015/11/14 PHP
php中array_slice和array_splice函数解析
2016/10/18 PHP
PHP7.1实现的AES与RSA加密操作示例
2018/06/15 PHP
php实现微信公众号企业转账功能
2018/10/01 PHP
动态加载js、css等文件跨iframe实现
2014/02/24 Javascript
JavaScript匿名函数与委托使用示例
2014/07/22 Javascript
jquery分隔Url的param方法(推荐)
2016/05/25 Javascript
jQuery1.9+中删除了live以后的替代方法
2016/06/17 Javascript
Node.js文件编码格式的转换的方法
2018/04/27 Javascript
webuploader分片上传的实现代码(前后端分离)
2018/09/10 Javascript
Vue使用NPM方式搭建项目
2018/10/25 Javascript
微信小程序Echarts覆盖正常组件问题解决
2019/07/13 Javascript
vue悬浮可拖拽悬浮按钮的实例代码
2019/08/20 Javascript
layer.open 子页面弹出层向父页面传输数据的例子
2019/09/26 Javascript
Python的Django框架安装全攻略
2015/07/15 Python
Django框架多表查询实例分析
2018/07/04 Python
关于Django ForeignKey 反向查询中filter和_set的效率对比详解
2018/12/15 Python
让你Python到很爽的加速递归函数的装饰器
2019/05/26 Python
python3 webp转gif格式的实现示例
2019/12/10 Python
计算Python Numpy向量之间的欧氏距离实例
2020/05/22 Python
如何快速一次性卸载所有python包(第三方库)呢
2020/10/20 Python
法雷奥SQA(electric)面试问题
2016/01/23 面试题
工商管理应届生求职信
2013/10/07 职场文书
教师现实表现材料
2014/02/14 职场文书
意向书范文
2014/03/31 职场文书
水毁工程实施方案
2014/04/01 职场文书
租房协议书范本
2014/04/09 职场文书
安全协议书范本
2014/04/21 职场文书
科技活动总结范文
2015/05/11 职场文书
小学数学教学反思范文
2016/02/16 职场文书
Matlab求解数组中的最大值及它所在的具体位置
2021/04/16 Python
WebRTC记录音视频流(web技术分享)
2022/02/24 Javascript