详谈JavaScript 匿名函数及闭包


Posted in Javascript onNovember 14, 2014

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

1.1 函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式

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

function double(x){

    return 2 * x;   

}

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

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

第三种:

var double = function(x) { return 2* x; }
注意“=”右边的函数就是一个匿名函数,创造完毕函数后,又将该函数赋给了变量square。

1.2 匿名函数的创建

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

第二种方式:

(function(x, y){

    alert(x + y);  

})(2, 3);

这里创建了一个匿名函数(在第一个括号内),第二个括号用于调用该匿名函数,并传入参数。

2、闭包
闭包的英文单词是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);   

 * }

 */

 

3、举例
匿名函数最大的用途是创建闭包(这是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会弹出递增的结果。

4、注意
4.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&rt;元素时,总是弹出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);

}

4.2 内存泄露

使用闭包十分容易造成浏览器的内存泄露,严重情况下会是浏览器挂死,感兴趣的的话可以参考:https://3water.com/article/57404.htm

Javascript 相关文章推荐
jQuery live( type, fn ) 委派事件实现
Oct 11 Javascript
随鼠标上下滚动的jquery代码
Dec 05 Javascript
JS 仿腾讯发表微博的效果代码
Dec 25 Javascript
使用requestAnimationFrame实现js动画性能好
Aug 06 Javascript
Javascript中的数组常用方法解析
Jun 17 Javascript
JS数组搜索之折半搜索实现方法分析
Mar 27 Javascript
关于使用axios的一些心得技巧分享
Jul 02 Javascript
微信小程序学习之数据处理详解
Jul 05 Javascript
vue页面跳转后返回原页面初始位置方法
Feb 11 Javascript
jquery ajaxfileuplod 上传文件 essyui laoding 效果【防止重复上传文件】
May 26 jQuery
微信小程序视图控件与bindtap之间的问题的解决
Apr 08 Javascript
Vue通过for循环随机生成不同的颜色或随机数的实例
Nov 09 Javascript
详谈JavaScript内存泄漏
Nov 14 #Javascript
js与C#进行时间戳转换
Nov 14 #Javascript
jquery ui bootstrap 实现自定义风格
Nov 14 #Javascript
使用node.js 制作网站前台后台
Nov 13 #Javascript
JavaScript 作用域链解析
Nov 13 #Javascript
jQuery $命名冲突解决方案汇总
Nov 13 #Javascript
js获取字符串最后一位方法汇总
Nov 13 #Javascript
You might like
PHP实现将多个文件中的内容合并为新文件的方法示例
2017/06/10 PHP
ThinkPHP中获取指定日期后工作日的具体日期方法
2018/10/14 PHP
PhpStorm 如何优雅的调试Hyperf的方法步骤
2019/11/24 PHP
用ASP将SQL搜索出来的内容导出为TXT的代码
2007/07/27 Javascript
jQuery操作input值的各种方法总结
2013/11/21 Javascript
改变隐藏的input中value值的方法
2014/03/19 Javascript
JS小游戏之仙剑翻牌源码详解
2014/09/25 Javascript
JavaScript模拟可展开、拖动与关闭的聊天窗口实例
2015/05/12 Javascript
jQuery实现返回顶部效果的方法
2015/05/29 Javascript
数据分析软件之FineReport教程:[5]参数界面JS(全)
2015/08/13 Javascript
程序员必知35个jQuery 代码片段
2015/11/05 Javascript
JavaScript编程中实现对象封装特性的实例讲解
2016/06/24 Javascript
Node.js 使用命令行工具检查更新
2017/06/08 Javascript
详解如何探测小程序返回到webview页面
2019/05/14 Javascript
如何使用three.js 制作一个三维的推箱子游戏
2020/07/29 Javascript
JS数据类型分类及常用判断方法
2020/11/19 Javascript
[02:19]DOTA2女子战队FOX视频专访:希望更多美眉一起加入
2013/10/15 DOTA
[44:21]Ti4 循环赛第四日 附加赛NEWBEE vs LGD
2014/07/13 DOTA
django模板语法学习之include示例详解
2017/12/17 Python
python3.5 tkinter实现页面跳转
2018/01/30 Python
Python实现读取Properties配置文件的方法
2018/03/29 Python
python最小生成树kruskal与prim算法详解
2019/01/17 Python
Python的条件锁与事件共享详解
2019/09/12 Python
python实现的读取网页并分词功能示例
2019/10/29 Python
全面介绍python中很常用的单元测试框架unitest
2020/12/14 Python
python利用文件时间批量重命名照片和视频
2021/02/09 Python
无谷物狗粮:Pooch & Mutt
2018/05/23 全球购物
Skyscanner香港:机票比价, 平机票和廉价航空机票预订
2020/02/07 全球购物
服装销售人员求职自我评价
2013/09/26 职场文书
公职人员索取回扣检举信
2014/04/04 职场文书
社区护士演讲稿
2014/08/27 职场文书
教师考核表个人总结
2015/02/12 职场文书
运动会表扬稿范文
2015/05/05 职场文书
MySQL性能压力基准测试工具sysbench的使用简介
2021/04/21 MySQL
Python集合的基础操作
2021/11/01 Python
Redis过期数据是否会被立马删除
2022/07/23 Redis