JavaScript中的函数表达式
在JavaScript中,函数是个非常重要的对象,函数通常有三种表现形式:函数声明,函数表达式和函数构造器创建的函数。
本文中主要看看函数表达式及其相关的知识点。
函数表达式
首先,看看函数表达式的表现形式,函数表达式(Function Expression, FE)有下面四个特点:
- 在代码中须出现在表达式的位置
- 有可选的函数名称
- 不会影响变量对象(VO)
- 在代码执行阶段创建
下面就通过一些例子来看看函数表达式的这四个特点。
FE特点分析
例子一:在下面代码中,"add"是一个函数对象,"sub"是一个普通JavaScript变量,但是被赋值了一个函数表达式" function (a, b){ return a - b; } ":
function add(a, b){
return a + b;
}
var sub = function (a, b){
return a - b;
}
console.log(add(1, 3));
//
console.log(sub(5, 1));
//
通过这个例子,可以直观的看到函数表达式的前两个特点:
- 在代码中须出现在表达式的位置
- " function (a, b){ return a - b; } "出现在了JavaScript语句中的表达式位置
- 有可选的函数名称
- " function (a, b){ return a - b; } "这个函数表达式没有函数名称,是个匿名函数表达式
例子二:为了解释函数表达式另外两个特点,继续看看下面的例子。
console.log(add(1, 3));
//
console.log(sub);
// undefined
console.log(sub(5, 1));
// Uncaught TypeError: sub is not a function(…) function add(a, b){
return a + b;
} var sub = function (a, b){
return a - b;
}
在这个例子中,调整了代码的执行顺序,这次函数"add"执行正常,但是对函数表达式的执行失败了。
对于这个例子,可以参考"JavaScript的执行上下文"一文中的内容,当代码开始执行的时候,可以得到下图所示的Global VO。

在Global VO中,对"add"函数表现为JavaScript的"Hoisting"效果,所以即使在"add"定义之前依然可以使用;
但是对于"sub"这个变量,根据"Execution Context"的初始化过程,"sub"会被初始化为"undefined",只有执行到" var sub = function (a, b){ return a - b; } "语句的时候,VO中的"sub"才会被赋值。
通过上面这个例子,可以看到了函数表达式的第四个特点
- 在代码执行阶段创建
例子三:对上面的例子进一步改动,这次给函数表达式加上了一个名字"_sub",也就是说,这里使用的是一个命名函数表达式。
var sub = function _sub(a, b){
console.log(typeof _sub);
return a - b;
}
console.log(sub(5, 1));
// function
//
console.log(typeof _sub)
// undefined
console.log(_sub(5, 1));
// Uncaught ReferenceError: _sub is not defined(…)
根据这段代码的运行结果,可以看到"_sub"这个函数名,只能在"_sub"这个函数内部使用;当在函数外部访问"_sub"的时候,就是得到"Uncaught ReferenceError: _sub is not defined(…)"错误。
所以通过这个可以看到函数表达式的第三个特点:
- 不会影响变量对象(VO)
FE的函数名
到了这里,肯定会有一个问题,"_sub"不在VO中,那在哪里?
其实对于命名函数表达式,JavaScript解释器额外的做了一些事情:
- 当解释器在代码执行阶段遇到命名函数表达式时,在函数表达式创建之前,解释器创建一个特定的辅助对象,并添加到当前作用域链的最前端
- 然后当解释器创建了函数表达式,在创建阶段,函数获取了[[Scope]] 属性(当前函数上下文的作用域链)
- 此后,函数表达式的函数名添加到特定对象上作为唯一的属性;这个属性的值是引用到函数表达式上
- 最后一步是从父作用域链中移除那个特定的对象
下面是表示这一过程的伪代码:
specialObject = {};
Scope = specialObject + Scope;
_sub = new FunctionExpression;
_sub.[[Scope]] = Scope;
specialObject. _sub = _sub; // {DontDelete}, {ReadOnly}
delete Scope[0]; // 从作用域链中删除特殊对象specialObject
函数递归
这一小节可能有些钻牛角尖,但是这里想演示递归调用可能出现的问题,以及通过命名函数表达式以更安全的方式执行递归。
下面看一个求阶乘的例子,由于函数对象也是可以被改变的,所以可能会出现下面的情况引起错误。
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
}
console.log(factorial(5))
//
newFunc = factorial
factorial = null
console.log(newFunc(5));
// Uncaught TypeError: factorial is not a function(…)
这时,可以利用函数的arguments对象的callee属性来解决上面的问题,也就是说在函数中,总是使用"arguments.callee"来递归调用函数。
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
但是上面的用法也有些问题,当在严格模式的时候"arguments.callee"就不能正常的工作了。
比较好的解决办法就是使用命名函数表达式,这样无论"factorial"怎么改变,都不会影响函数表达式" function f(num){…} "
var factorial = (function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
});
代码模块化
在JavaScript中,没有块作用域,只有函数作用域,函数内部可以访问外部的变量和函数,但是函数内部的变量和函数在函数外是不能访问的。
所以,通过函数(通常直接使用函数表达式),可以模块化JavaScript代码。
创建模块
为了能够到达下面的目的,我们可以通过函数表达式来建立模块。
- 创建一个可以重用的代码模块
- 模块中封装了使用者不必关心的内容,只暴露提供给使用者的接口
- 尽量与全局namespace进行隔离,减少对全局namespace的污染
下面看一个简单的例子:
var Calc = (function(){
var _a, _b;
return{
add: function(){
return _a + _b;
},
sub: function(){
return _a - _b;
},
set: function(a, b){
_a = a;
_b = b;
}
}
}());
Calc.set(10, 4);
console.log(Calc.add());
//
console.log(Calc.sub());
//
代码中通过匿名函数表达式创建了一个"Calc"模块,这是一种常用的创建模块的方式:
- 创建一个匿名函数表达式,这个函数表达式中包含了模块自身的私有变量和函数;
- 通过执行这个函数表达式可以得到一个对象,对象中包含了模块想要暴露给用户的公共接口。
除了返回一个对象的方式,有的模块也会使用另外一种方式,将包含模块公共接口的对象作为全局变量的一个属性。
这样在代码的其他地方,就可以直接通过全局变量的这个属性来使用模块了。
例如下面的例子:
(function(){
var _a, _b;
var root = this;
var _ = {
add: function(){
return _a + _b;
},
sub: function(){
return _a - _b;
},
set: function(a, b){
_a = a;
_b = b;
}
}
root._ = _;
}.call(this));
_.set(10, 4);
console.log(_.add());
//
console.log(_.sub());
//
立即调用的函数表达式
在上面两个例子中,都使用了匿名的函数表达式,并且都是立即执行的。如果去看看JavaScript一些开源库的代码,例如JQuery、underscore等等,都会发现类似的立即执行的匿名函数代码。
立即调用的函数表达式通常表现为下面的形式:
(function () {
/* code */
})();
(function () {
/* code */
} ());
在underscore这个JavaScript库中,使用的是下面的方式:
(function () {
// Establish the root object, `window` in the browser, or `exports` on the server.
var root = this;
/* code */
} .call(this));
在这里,underscore模块直接对全局变量this进行了缓存,方便模块内部使用。
总结
本文简单介绍了JavaScript中的函数表达式,并通过三个例子解释了函数表达式的四个特点。
- 在代码中须出现在表达式的位置
- 有可选的函数名称
- 不会影响变量对象(VO)
- 在代码执行阶段创建
通过函数表达式可以方便的建立JavaScript模块,通过模块可以实现下面的效果:
- 创建一个可以重用的代码模块
- 模块中封装了使用者不必关心的内容,只暴露提供给使用者的接口
- 尽量与全局namespace进行隔离,减少对全局namespace的污染
JavaScript中的函数表达式的更多相关文章
- 【JavaScript】Javascript中的函数声明和函数表达式
Javascript有很多有趣的用法,在Google Code Search里能找到不少,举一个例子: <script> ~function() { alert("hello, ...
- JavaScript中Eval()函数的作用
这一周感觉没什么写的,不过在研究dwz源码的时候有一个eval()的方法不是很了解,分享出来一起学习 -->首先来个最简单的理解 eval可以将字符串生成语句执行,和SQL的exec()类似. ...
- 深入理解javascript:揭秘命名函数表达式
这是一篇转自汤姆大叔的文章:http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html 前言 网上还没用发现有人对命名函数表达式进去重复深 ...
- Javascript中的函数
Javascript中的函数 1.什么是函数 函数是被命名的,独立的,完成特定功能的代码段.其可能给调用它的程序返回值,我们把这个代码段就称之为"函数". 被命名的:函数大部分都是 ...
- Javascript中的函数数学运算
1.Math函数与属性使用语法 Math.方法名(参数1,参数2,...); Math.属性; 说明 Math函数可以没有参数,比如Math.random()函数,或有多个参数,比如Math.max( ...
- JavaScript中的函数(一)
javaScript中的函数实际上是对象,每一个函数都是Function类型的实例,和其他引用类型一样具有属性和方法.由于函数是对象,因此函数名实际上也就是一个指向函数对象的指针,也就是函数对象的一个 ...
- 如何理解JavaScript中的函数
转: 如何理解JavaScript中的函数 JS中的函数简介 JS中的函数是一种通过调用来完成具体业务的一段代码块.最核心的目的是将可重复执行的操作进行封装,然后供调用方无限制的调用. JS中的函数的 ...
- JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解
二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...
- 前端学习 第六弹: javascript中的函数与闭包
前端学习 第六弹: javascript中的函数与闭包 当function里嵌套function时,内部的function可以访问外部function里的变量 function foo(x) { ...
随机推荐
- jQuery TimeCircles 倒计时
在线实例 默认 倒计时 使用方法 <h1 style="margin: 40px; font: 32px Microsoft Yahei; text-align: center;&qu ...
- 手机端Swiper 触屏滑动
在线实例 默认 响应式 垂直 空间间隔 滚动 自动滚动 中心化 中心化自动 免费模式 多个滚动 水平滚动 grab-cursor 使用方法 <div class="swiper-con ...
- HTML5 Canvas 高仿逼真 3D 布料图案效果
HTML5 规范引进了很多新特性,其中最令人期待的之一就是 Canvas 元素,HTML5 Canvas 提供了通过 JavaScript 绘制图形的方法,非常强大.下面给大家分享一个 HTML5 C ...
- slid.es – 创建在线幻灯片和演示文稿的最佳途径
slid.es 提供了一种创建在线幻灯片和演示文稿的简单方法,让你通过几个简单的步骤制作效果精美的在线演示文稿.基于 HTML5 和 CSS3 实现,在现代浏览器中效果最佳. 您可能感兴趣的相关文章 ...
- [html5+java]文件异步读取及上传核心代码
html5+java 文件异步读取及上传关键代码段 功能: 1.多文件文件拖拽上传,file input 多文件选择 2.html5 File Api 异步FormData,blob上传,图片显示 3 ...
- JavaScript学习笔记-对象
枚举对象的属性:通常用for(...in...)来循环遍历,由于 for in 总是要遍历整个原型链,因此如果一个对象的继承层次太深的话会影响性能 for(var i in foo){ if(foo. ...
- JavaScript学习笔记-面向对象的模块化编程
面向对象的模块化编程 模块是一个独立的JS文件,模块文件可以包含一个类定义.一组相关的类.一个实用函数库.一些待执行的代码 模块化的目标:支持大规模的程序开发,处理分散源代码的组装,并能让代码正确执行 ...
- servle
基于HTTP协议下的,http请求和http响应. http请求------请求的是服务器中的地方. 1.servlet就是解析http请求和发送http响应. 2.servlet是是一个 ...
- 使用MDK3探测隐藏SSID名称
先将网卡设置为监听模式. airmon-ng start wlan0 扫描附近无线网络,找出隐藏SSID的AP. airodump-ng wlan0mon 这里用下图搜到的第一个隐藏SSID做测试. ...
- JavaSE 和 JavaEE 的关系
1.javaSe看成是java基础 (Standard Edition(标准版) ) 2.J2EE看成是应用 (Enterprise Edition(企业版)) 3.java ...