javaee论坛

普通会员

225648

帖子

353

回复

367

积分

楼主
发表于 2017-08-28 03:31:22 | 查看: 522 | 回复: 1
(function () { /* code */ })(); // 但是这个也是可以用的

// 由于括弧()和JS的&&,异或,逗号等操作符是在函数表达式和函数声明上消除歧义的
// 所以一旦解析器知道其中一个已经是表达式了,其它的也都默认为表达式了
var i = function () { return 10; } ();
true && function () { /* code */ } ();
0, function () { /* code */ } ();

// 如果你不在意返回值,或者不怕难以阅读
// 你甚至可以在function前面加一元操作符号

!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();

用闭包保存状态

和普通function执行的时候传参数一样,自执行的函数表达式也可以这么传参,因为闭包直接可以引用传入的这些参数,利用这些被lock住的传入参数,自执行函数表达式可以有效地保存状态。

// 这个代码是错误的,因为变量i从来就没背locked住// 相反,当循环执行以后,我们在点击的时候i才获得数值// 因为这个时候i操真正获得值// 所以说无论点击那个连接,最终显示的都是I am link #10(如果有10个a元素的话)var elems = document.getElementsByTagName('a');for (var i = 0; i < elems.length; i++) {    elems[i].addEventListener('click', function (e) {        e.preventDefault();        alert('I am link #' + i);    }, 'false');}// 这个是可以用的,因为他在自执行函数表达式闭包内部// i的值作为locked的索引存在,在循环执行结束以后,尽管最后i的值变成了a元素总数(例如10)// 但闭包内部的lockedInIndex值是没有改变,因为他已经执行完毕了// 所以当点击连接的时候,结果是正确的var elems = document.getElementsByTagName('a');for (var i = 0; i < elems.length; i++) {    (function (lockedInIndex) {        elems[i].addEventListener('click', function (e) {            e.preventDefault();            alert('I am link #' + lockedInIndex);        }, 'false');    })(i);}// 你也可以像下面这样应用,在处理函数那里使用自执行函数表达式// 而不是在addEventListener外部// 但是相对来说,上面的代码更具可读性var elems = document.getElementsByTagName('a');for (var i = 0; i < elems.length; i++) {    elems[i].addEventListener('click', (function (lockedInIndex) {        return function (e) {            e.preventDefault();            alert('I am link #' + lockedInIndex);        };    })(i), 'false');}

其实,上面2个例子里的lockedInIndex变量,也可以换成i,因为和外面的i不在一个作用于,所以不会出现问题,这也是匿名函数+闭包的威力。

自执行匿名函数和立即执行的函数表达式区别

在这篇帖子里,我们一直叫自执行函数,确切的说是自执行匿名函数(Self-executing anonymous function),但英文原文作者一直倡议使用立即调用的函数表达式(Immediately-Invoked Function Expression)这一名称,作者又举了一堆例子来解释,好吧,我们来看看:

// 这是一个自执行的函数,函数内部执行自身,递归function foo() { foo(); }// 这是一个自执行的匿名函数,因为没有标示名称// 必须使用arguments.callee属性来执行自己var foo = function () { arguments.callee(); };// 这可能也是一个自执行的匿名函数,仅仅是foo标示名称引用它自身// 如果你将foo改变成其它的,你将得到一个used-to-self-execute匿名函数var foo = function () { foo(); };// 有些人叫这个是自执行的匿名函数(即便它不是),因为它没有调用自身,它只是立即执行而已。(function () { /* code */ } ());// 为函数表达式添加一个标示名称,可以方便Debug// 但一定命名了,这个函数就不再是匿名的了(function foo() { /* code */ } ());// 立即调用的函数表达式(IIFE)也可以自执行,不过可能不常用罢了(function () { arguments.callee(); } ());(function foo() { foo(); } ());// 另外,下面的代码在黑莓5里执行会出错,因为在一个命名的函数表达式里,他的名称是undefined// 呵呵,奇怪(function foo() { foo(); } ());

希望这里的一些例子,可以让大家明白,什么叫自执行,什么叫立即调用。

注:arguments.callee在ECMAScript 5 strict mode里被废弃了,所以在这个模式下,其实是不能用的。

Module模式

在讲到这个立即调用的函数表达式的时候,我又想起来了Module模式,如果你还不熟悉这个模式,我们先来看看代码:

// 创建一个立即调用的匿名函数表达式// return一个变量,其中这个变量里包含你要暴露的东西// 返回的这个变量将赋值给counter,而不是外面声明的function自身var counter = (function () {    var i = 0;    return {        get: function () {            return i;        },        set: function (val) {            i = val;        },        increment: function () {            return ++i;        }    };} ());// counter是一个带有多个属性的对象,上面的代码对于属性的体现其实是方法counter.get(); // 0counter.set(3);counter.increment(); // 4counter.increment(); // 5counter.i; // undefined 因为i不是返回对象的属性i; // 引用错误: i 没有定义(因为i只存在于闭包)

更多阅读

希望上面的一些例子,能让你对立即调用的函数表达(也就是我们所说的自执行函数)有所了解,如果你想了解更多关于function和Module模式的信息,请继续访问下面列出的网站:

  1. ECMA-262-3 in detail. Chapter 5. Functions. - Dmitry A. Soshnikov
  2. Functions and function scope - Mozilla Developer Network
  3. Named function expressions - Juriy “kangax” Zaytsev
  4. 全面解析Module模式- Ben Cherry(大叔翻译整理)
  5. Closures explained with JavaScript - Nick Morgan

同步与推荐

本文已同步至目录索引:深入理解JavaScript系列

转载自





普通会员

0

帖子

341

回复

353

积分
沙发
发表于 2024-05-03 14:19:59

如果这就是爱,再转身的时候就该留下

您需要登录后才可以回帖 登录 | 立即注册

触屏版| 电脑版

技术支持 历史网 V2.0 © 2016-2017