老生常谈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 相关文章推荐
Lazy Load 延迟加载图片的 jQuery 插件
Feb 06 Javascript
JavaScript和CSS通过expression实现Table居中显示
Jun 28 Javascript
js中传递特殊字符(+,&amp;)的方法
Jan 16 Javascript
javascript将相对路径转绝对路径示例
Mar 14 Javascript
jQuery制作的别致导航有阴影背景高亮模式窗口
Apr 15 Javascript
javascript继承的六大模式小结
Apr 13 Javascript
jQuery实现表格展开与折叠的方法
May 04 Javascript
jQuery移动端图片上传组件
Jun 12 Javascript
JS实现给对象动态添加属性的方法
Jan 05 Javascript
webpack配置proxyTable时pathRewrite无效的解决方法
Dec 13 Javascript
element-ui上传一张图片后隐藏上传按钮功能
May 22 Javascript
Vue scoped及deep使用方法解析
Aug 01 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程序总是提示验证码输入有误解决方案
2015/01/07 PHP
PHP中把数据库查询结果输出为json格式简单实例
2015/04/09 PHP
PHP面向对象程序设计重载(overloading)操作详解
2019/06/13 PHP
js中function()使用方法
2013/12/24 Javascript
动态加载脚本提升javascript性能
2014/02/24 Javascript
动态载入js提高网页打开速度的方法
2014/07/04 Javascript
jquery 插件实现多行文本框[textarea]自动高度
2015/03/04 Javascript
jQuery实现调整表格单列顺序完整实例
2016/06/20 Javascript
前端设计师们最常用的JS代码汇总
2016/09/25 Javascript
通过扫描二维码打开app的实现代码
2016/11/10 Javascript
详解nodejs微信公众号开发——4.自动回复各种消息
2017/04/11 NodeJs
Vue中建立全局引用或者全局命令的方法
2017/08/21 Javascript
jQuery实现表单动态添加与删除数据操作示例
2018/07/03 jQuery
create-react-app使用antd按需加载的样式无效问题的解决
2019/02/26 Javascript
node实现简单的增删改查接口实例代码
2019/08/22 Javascript
javascript数组的定义及操作实例
2019/11/10 Javascript
layui table表格数据的新增,修改,删除,查询,双击获取行数据方式
2019/11/14 Javascript
jQuery实现轮播图效果demo
2020/01/11 jQuery
vue+element-ui表格封装tag标签使用插槽
2020/06/18 Javascript
使用python加密自己的密码
2015/08/04 Python
python自定义函数实现一个数的三次方计算方法
2019/01/20 Python
Python查找数组中数值和下标相等的元素示例【二分查找】
2019/02/13 Python
python获取磁盘号下盘符步骤详解
2019/06/19 Python
基于多进程中APScheduler重复运行的解决方法
2019/07/22 Python
Python内建序列通用操作6种实现方法
2020/03/26 Python
Python之变量类型和if判断方式
2020/05/05 Python
Python scrapy爬取小说代码案例详解
2020/07/09 Python
利用HTML5绘制点线面组成的3D图形的示例
2015/05/12 HTML / CSS
使用html5 canvas 画时钟代码实例分享
2015/11/11 HTML / CSS
美国最值得信赖的宠物药房:Allivet
2019/03/23 全球购物
党的群众路线教育实践活动批评与自我批评
2014/02/16 职场文书
餐饮服务食品安全责任书
2014/07/25 职场文书
家庭教育培训学习心得体会
2016/01/14 职场文书
干货干货!2019最新优秀创业计划书
2019/03/21 职场文书
2019暑假学生安全口号
2019/06/27 职场文书
幽默口才训练经典句子(48句)
2019/08/19 职场文书