浅析Javascript匿名函数与自执行函数


Posted in Javascript onFebruary 06, 2016

函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途。匿名函数:就是没有函数名的函数。

函数的定义,大致可分为三种方式:

第一种:这也是最常规的一种

function double(x){ 
return 2 * x; 
}

第二种:这种方法使用了Function构造函数,把参数列表和函数体都作为字符串,很不方便,不建议使用。

var double = new Function('x', 'return 2 * x;');

第三种:

var double = function(x) { return 2* x; }

注意“=”右边的函数就是一个匿名函数,创造完毕函数后,又将该函数赋给了变量square。

匿名函数的创建

第一种方式:就是上面所讲的定义square函数,这也是最常用的方式之一。

第二种方式:

(function(x, y){ 
alert(x + y); 
})(2, 3);

这里创建了一个匿名函数(在第一个括号内),第二个括号用于调用该匿名函数,并传入参数。括号是表达式,是表达式就有返回值,所以可以在后面加一对括号让它们执行.

自执行的匿名函数

1. 什么是自执行的匿名函数?

它是指形如这样的函数: (function {// code})();

2. 疑问

为什么(function {// code})();可以被执行, 而function {// code}();却会报错?

3. 分析

(1). 首先, 要清楚两者的区别:
(function {// code})是表达式, function {// code}是函数声明.
(2). 其次, js"预编译"的特点:
js在"预编译"阶段, 会解释函数声明, 但却会忽略表式.
(3). 当js执行到function() {//code}();时, 由于function() {//code}在"预编译"阶段已经被解释过, js会跳过function(){//code}, 试图去执行();, 故会报错;
当js执行到(function {// code})();时, 由于(function {// code})是表达式, js会去对它求解得到返回值, 由于返回值是一 个函数, 故而遇到();时, 便会被执行.

另外, 函数转换为表达式的方法并不一定要靠分组操作符(),我们还可以用void操作符,~操作符,!操作符……

如:

!function(){ 
alert("另类的匿名函数自执行"); 
}();

匿名函数与闭包

闭包的英文单词是closure,这是JavaScript中非常重要的一部分知识,因为使用闭包可以大大减少我们的代码量,使我们的代码看上去更加清晰等等,总之功能十分强大。

闭包的含义:闭包说白了就是函数的嵌套,内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕(这点涉及JavaScript作用域链)。

function checkClosure(){ 
var str = 'rain-man'; 
setTimeout( 
function(){ alert(str); } //这是一个匿名函数 
, 2000); 
} 
checkClosure();

这个例子看上去十分的简单,仔细分析下它的执行过程还是有许多知识点的:checkClosure函数的执行是瞬间的(也许用时只是0.00001毫秒),在checkClosure的函数体内创建了一个变量str,在checkClosure执行完毕之后str并没有被释放,这是因为setTimeout内的匿名函数存在这对str的引用。待到2秒后函数体内的匿名函数被执行完毕,str才被释放。

用闭包来优化代码:

function forTimeout(x, y){ 
alert(x + y); 
} 
function delay(x , y , time){ 
setTimeout('forTimeout(' + x + ',' + y + ')' , time); 
} 
/** 
* 上面的delay函数十分难以阅读,也不容易编写,但如果使用闭包就可以让代码更加清晰 
* function delay(x , y , time){ 
* setTimeout( 
* function(){ 
* forTimeout(x , y) 
* } 
* , time); 
* } 
*/

匿名函数最大的用途是创建闭包(这是JavaScript语言的特性之一),并且还可以构建命名空间,以减少全局变量的使用。

var oEvent = {}; 
(function(){ 
var addEvent = function(){ /*代码的实现省略了*/ }; 
function removeEvent(){} 

oEvent.addEvent = addEvent; 
oEvent.removeEvent = removeEvent; 
})();

在这段代码中函数addEvent和removeEvent都是局部变量,但我们可以通过全局变量oEvent使用它,这就大大减少了全局变量的使用,增强了网页的安全性。

我们要想使用此段代码:

oEvent.addEvent(document.getElementById('box') , 'click' , function(){});
var rainman = (function(x , y){ 
return x + y; 
})(2 , 3); 
/** 
* 也可以写成下面的形式,因为第一个括号只是帮助我们阅读,但是不推荐使用下面这种书写格式。 
* var rainman = function(x , y){ 
* return x + y; 
* }(2 , 3);

在这里我们创建了一个变量rainman,并通过直接调用匿名函数初始化为5,这种小技巧有时十分实用。

var outer = null; 
(function(){ 
var one = 1; 
function inner (){ 
one += 1; 
alert(one); 
} 
outer = inner; 
})(); 
outer(); //2 
outer(); //3 
outer(); //4

这段代码中的变量one是一个局部变量(因为它被定义在一个函数之内),因此外部是不可以访问的。但是这里我们创建了inner函数,inner函数是可以访问变量one的;又将全局变量outer引用了inner,所以三次调用outer会弹出递增的结果。

注意

1 闭包允许内层函数引用父函数中的变量,但是该变量是最终值

/** 
* <body> 
* <ul> 
* <li>one</li> 
* <li>two</li> 
* <li>three</li> 
* <li>one</li> 
* </ul> 
*/ 
var lists = document.getElementsByTagName('li'); 
for(var i = 0 , len = lists.length ; i < len ; i++){ 
lists[ i ].onmouseover = function(){ 
alert(i); 
}; 
}

你会发现当鼠标移过每一个<li>元素时,总是弹出4,而不是我们期待的元素下标。这是为什么呢?注意事项里已经讲了(最终值)。显然这种解释过于简单,当mouseover事件调用监听函数时,首先在匿名函数( function(){ alert(i); })内部查找是否定义了 i,结果是没有定义;因此它会向上查找,查找结果是已经定义了,并且i的值是4(循环后的i值);所以,最终每次弹出的都是4。

解决方法一:

var lists = document.getElementsByTagName('li'); 
for(var i = 0 , len = lists.length ; i < len ; i++){ 
(function(index){ 
lists[ index ].onmouseover = function(){ 
alert(index); 
}; 
})(i); 
}

解决方法二:

var lists = document.getElementsByTagName('li'); 
for(var i = 0, len = lists.length; i < len; i++){ 
lists[ i ].$$index = i; //通过在Dom元素上绑定$$index属性记录下标 
lists[ i ].onmouseover = function(){ 
alert(this.$$index); 
}; 
}

解决方法三:

function eventListener(list, index){ 
list.onmouseover = function(){ 
alert(index); 
}; 
} 
var lists = document.getElementsByTagName('li'); 
for(var i = 0 , len = lists.length ; i < len ; i++){ 
eventListener(lists[ i ] , i); 
}

2 内存泄露

使用闭包十分容易造成浏览器的内存泄露,严重情况下会是浏览器挂死

Javascript 相关文章推荐
javascript实现状态栏中文字动态显示的方法
Oct 20 Javascript
JavaScript三种绑定事件方式及相互之间的区别分析
Jan 10 Javascript
JavaScript中无法通过div.style.left获取值的解决方法
Feb 19 Javascript
使用AngularJS2中的指令实现按钮的切换效果
Mar 27 Javascript
vue 自定义全局方法,在组件里面的使用介绍
Feb 28 Javascript
使用async await 封装 axios的方法
Jul 09 Javascript
vue-cli项目中使用echarts图表实例
Oct 22 Javascript
React 实现拖拽功能的示例代码
Jan 06 Javascript
layui输入框只允许输入中文且判断长度的例子
Sep 18 Javascript
在Express中提供静态文件的实现方法
Oct 17 Javascript
浅谈Three.js截图并下载的大坑
Nov 01 Javascript
基于JQuery和DWR实现异步数据传递
Oct 16 jQuery
JS通过Cookie判断页面是否为首次打开
Feb 05 #Javascript
JavaScript判断图片是否已经加载完毕的方法汇总
Feb 05 #Javascript
jquery实现的判断倒计时是否结束代码
Feb 05 #Javascript
AngularJs中route的使用方法和配置
Feb 04 #Javascript
JS获取时间的相关函数及时间戳与时间日期之间的转换
Feb 04 #Javascript
分享我对JS插件开发的一些感想和心得
Feb 04 #Javascript
关于JavaScript作用域你想知道的一切
Feb 04 #Javascript
You might like
DOTA2 探索永无止境 玩家自创强悍插眼攻略
2020/04/20 DOTA
谈谈你对Zend SAPIs(Zend SAPI Internals)的理解
2015/11/10 PHP
如何写php守护进程(Daemon)
2015/12/30 PHP
Ajax实现对静态页面的文章访问统计功能示例
2016/10/10 PHP
ThinkPHP5实现作业管理系统中处理学生未交作业与已交作业信息的方法
2016/11/12 PHP
基于Codeigniter框架实现的student信息系统站点动态发布功能详解
2017/03/23 PHP
javascript调试说明
2010/06/07 Javascript
jquery交替变换颜色的三种方法 实例代码
2013/11/19 Javascript
自定义jquery模态窗口插件无法在顶层窗口显示问题
2014/05/29 Javascript
JavaScript中Function()函数的使用教程
2015/06/04 Javascript
基于jQuery仿淘宝产品图片放大镜代码分享
2020/06/23 Javascript
vue.js入门教程之基础语法小结
2016/09/01 Javascript
nodejs微信公众号支付开发
2016/09/19 NodeJs
基于Vue2的移动端开发环境搭建详解
2016/11/03 Javascript
Easyui Datagrid自定义按钮列(最后面的操作列)
2017/07/13 Javascript
JS解决IOS中拍照图片预览旋转90度BUG的问题
2017/09/13 Javascript
laydate日历控件使用方法详解
2017/11/20 Javascript
vue将对象新增的属性添加到检测序列的方法
2018/02/24 Javascript
深入理解JavaScript的async/await
2018/08/05 Javascript
JS获取当前时间的年月日时分秒及时间的格式化的方法
2019/12/18 Javascript
js实现网页随机验证码
2020/10/19 Javascript
kNN算法python实现和简单数字识别的方法
2014/11/18 Python
python下实现二叉堆以及堆排序的示例
2017/09/29 Python
Python实现利用最大公约数求三个正整数的最小公倍数示例
2017/09/30 Python
Python列表常见操作详解(获取,增加,删除,修改,排序等)
2019/02/18 Python
python中matplotlib条件背景颜色的实现
2019/09/02 Python
django多种支付、并发订单处理实例代码
2019/12/13 Python
Pycharm配置lua编译环境过程图解
2020/11/28 Python
PyTorch 中的傅里叶卷积实现示例
2020/12/11 Python
英国最大的笔记本电脑直销专家:Laptops Direct
2019/07/20 全球购物
生物技术专业研究生自荐信
2013/09/22 职场文书
优秀的自荐信要注意哪些
2014/01/03 职场文书
高三语文教学反思
2014/01/15 职场文书
企业文化口号
2014/06/12 职场文书
Python 数据可视化之Bokeh详解
2021/11/02 Python
详细聊聊Oracle表碎片对性能有多大的影响
2022/03/19 Oracle