老生常谈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 相关文章推荐
js或css文件后面跟参数的原因说明
Jan 09 Javascript
jQuery数组处理方法汇总
Jun 20 Javascript
jquery模拟按下回车实现代码
Sep 20 Javascript
原生javascript实现的一个简单动画效果
Mar 30 Javascript
详解Angular路由 ng-route和ui-router的区别
May 22 Javascript
jquery.pagination.js分页使用教程
Oct 23 jQuery
vue+axios 前端实现登录拦截的两种方式(路由拦截、http拦截)
Oct 24 Javascript
JS实现求字符串中出现最多次数的字符和次数示例
Jul 05 Javascript
element-ui中Table表格省市区合并单元格的方法实现
Aug 07 Javascript
过滤器vue.filters的使用方法实现
Sep 18 Javascript
Vue 如何使用props、emit实现自定义双向绑定的实现
Jun 05 Javascript
JS面向对象实现飞机大战
Aug 26 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
用PHP编程语言开发动态WAP页面
2006/10/09 PHP
使用XHGui来测试PHP性能的教程
2015/07/03 PHP
thinkPHP下的widget扩展用法实例分析
2015/12/26 PHP
php阳历转农历优化版
2016/08/08 PHP
tp5 sum某个字段相加得到总数的例子
2019/10/18 PHP
JavaScript 异步方法队列链实现代码分析
2010/06/05 Javascript
juqery 学习之三 选择器 简单 内容
2010/11/25 Javascript
js变换显示图片的实例
2013/04/16 Javascript
ie 7/8不支持trim的属性的解决方案
2014/05/23 Javascript
JS实现超过长度限制后自动跳转下一款文本框的方法
2015/02/23 Javascript
JavaScript 动态加载脚本和样式的方法
2015/04/13 Javascript
BootStrap CSS全局样式和表格样式源码解析
2017/01/20 Javascript
JS时间控制实现动态效果的实例讲解
2017/07/31 Javascript
vue组件实践之可搜索下拉框功能
2018/11/25 Javascript
js+canvas实现简单扫雷小游戏
2021/01/22 Javascript
Vue $emit()不能触发父组件方法的原因及解决
2020/07/28 Javascript
vue+elementui通用弹窗的实现(新增+编辑)
2021/01/07 Vue.js
[06:59]DOTA2-DPC中国联赛3月7日Recap集锦
2021/03/11 DOTA
python中lambda函数 list comprehension 和 zip函数使用指南
2014/09/28 Python
Python多维/嵌套字典数据无限遍历的实现
2016/11/04 Python
python3使用pyqt5制作一个超简单浏览器的实例
2017/10/19 Python
对python 矩阵转置transpose的实例讲解
2018/04/17 Python
Python3.6.0+opencv3.3.0人脸检测示例
2018/05/25 Python
Python中的单下划线和双下划线使用场景详解
2019/09/09 Python
Python数组并集交集补集代码实例
2020/02/18 Python
tensorflow之读取jpg图像长和宽实例
2020/06/18 Python
Mio Skincare英国官网:身体紧致及孕期身体护理
2018/08/19 全球购物
数控技术应届生求职信
2013/11/13 职场文书
应届医学毕业生求职信分享
2013/12/02 职场文书
土建专业大学生自荐信范文
2014/04/09 职场文书
2014年财政局工作总结
2014/12/09 职场文书
巾帼标兵事迹材料
2014/12/26 职场文书
毕业生爱心捐书倡议书
2015/04/27 职场文书
浅谈Web Storage API的使用
2021/06/23 Javascript
Redis入门教程详解
2021/08/30 Redis
Java版 简易五子棋小游戏
2022/05/04 Java/Android