javascript匿名函数的各种执行形式

简介:

 近期在研究Pomelo源码,这个框架基于Node.js,所以非要频繁地与JavaScript脚本打交道不可。因此,本文中我们来总结

javascript语言中匿名函数的主要目的及各种存在形式。其实,匿名函数在许多语言中都有提供,这个词语各位应该不陌生。

 

一、函数与匿名函数
    
    首先,我们来看一下在javascript中正常函数定义的语法:

1
2
3
4
function  functionname(var1,var2,...,varX)
{
     //...函数体
}

   如上所示,正常函数的定义需要一个函数名来标识该函数对象,有了这个标识,我们就可以在其他地方(作用域范围内)对它进行调用

   那么,什么是匿名函数呢?匿名匿名就是没有名字,在这里就是没有函数名。那么是不是可以直接把上面代码的函数名去掉呢?答案是

显然是否定的。因为上面不是一个执行表达式,而是一个函数定义体,直接去掉函数名就会报错。
如下面的代码就会报"function statement requires a name"的错误。

1
2
3
4
5
6
7
8
9
10
11
< html >
     < head >
         < title >test</ title >
         < scripttype = "text/javascript" >
             function(){
             }
         </ script >
     </ head >
     < body >
     </ body >
</ html >

    虽然上面的代码是会出错,但是我们仍然不可否认那就是一个匿名函数了,只是匿名函数的出现和执行的形式有一些要求,而上面就

是不符合使用要求的一种,详细情况我们后面再介绍。

 

二、匿名函数的好处

 

匿名函数的好处至少有如下两点:


1.减少全局变量(或者某作用域内变量)的污染;
2.使代码结构更加优美,如代码可以实现块状分布。

 

对于上述两点,相信各位不难理解。对于第2点,我们知道其他许多语言中往往提供块状语言中的局部变量支持(例如C语言中的{}块内的局部变量定义),而在JS中正可以利用匿名函数的技巧来实现类似于其他高级语言中的上述功能。

 

三、匿名函数的各种执行形式

 

1.通过括号运算符(分组运算)使匿名函数执行定义并执行定义后的函数。

1
( function (){}());

 

2.通过括号运算符(分组运算)使匿名函数执行定义,然后再执行该函数

1
( function (){})();

 

3.通过在function前面加运算符(如!,+,-等)来使匿名函数定义并执行

1
function (){}();

 

4.通过new实例化对象来执行匿名函数

1
new  function (){};

 

5.用void关键字来使匿名函数执行

1
void  function (){}();

 

其实,当我们把new和void改成其他关键字时,比如delete,你会发现代码依然很好地执行。也就是说,只要我们在function前面加上合

法的关键字就可以让它不报错并执行了。

 

【补充】关于自调用函数。看下面的例子:

1
2
3
4
5
6
7
( function (a,b){
 
     var  result = a+b;
 
     return  result;
 
})(10,20);

这里定义的函数称为“自调用函数”,当然里面是一个匿名函数。这段代码不仅定义了函数,而且立即使用指定的参数执行。根据本人粗浅经验,这是许多JS框架中常用的一种形式,大家注意。

 

四、小结

 

    没有办法,要想学习新框架肯定要适应开发者的技术背景及设计理念等等。我们的编程习惯在于我们自己,而要跑在别人后面学习只能先熟悉他的方式才能有所学。注意:上述各种javascript匿名函数使用方式在你阅读几乎所有基于JS的源码时都会遇到。大家可以注意下面几个例子。

 

    例1:来自于Express.js(Express这个框架是基于Node.js的著名Web开发框架)

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  'json' ,
   'urlencoded' ,
   'bodyParser' ,
   'compress' ,
   'cookieSession' ,
   'session' ,
   'logger' ,
   'cookieParser' ,
   'favicon' ,
   'responseTime' ,
   'errorHandler' ,
   'timeout' ,
   'methodOverride' ,
   'vhost' ,
   'csrf' ,
   'directory' ,
   'limit' ,
   'multipart' ,
   'staticCache' ,
].forEach( function  (name) {
   Object.defineProperty(exports, name, {
     get:  function  () {
       throw  new  Error( 'Most middleware (like '  + name +  ') is no longer bundled with Express and must be installed separately. Please see 
https://github.com/senchalabs/connect#middleware.'
);
     },
     configurable:  true
   });
});

  例2:来自于著名socket.io.js框架(这个长达近4000行的文件中并列定义了类似于下面的十多个匿名函数)。

  注意:这个模块文件在加载时相应的匿名函数是要执行的(而不是只定义匿名函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/**
    * There is a way to hide the loading indicator in Firefox. If you create and
    * remove a iframe it will stop showing the current loading indicator.
    * Unfortunately we can't feature detect that and UA sniffing is evil.
    *
    * @api private
    */
   var  indicator = global.document &&  "MozAppearance"  in
     global.document.documentElement.style;
   /**
    * Expose constructor.
    */
   exports['jsonp-polling '] = JSONPPolling;
   /**
    * The JSONP transport creates an persistent connection by dynamically
    * inserting a script tag in the page. This script tag will receive the
    * information of the Socket.IO server. When new information is received
    * it creates a new script tag for the new data stream.
    *
    * @constructor
    * @extends {io.Transport.xhr-polling}
    * @api public
    */
   function JSONPPolling (socket) {
     io.Transport[' xhr-polling '].apply(this, arguments);
     this.index = io.j.length;
     var self = this;
     io.j.push(function (msg) {
       self._(msg);
     });
   };
   /**
    * Inherits from XHR polling transport.
    */
   io.util.inherit(JSONPPolling, io.Transport[' xhr-polling ']);
   /**
    * Transport name
    *
    * @api public
    */
   JSONPPolling.prototype.name = ' jsonp-polling ';
   /**
    * Posts a encoded message to the Socket.IO server using an iframe.
    * The iframe is used because script tags can create POST based requests.
    * The iframe is positioned outside of the view so the user does not
    * notice it' s existence.
    *
    * @param {String} data A encoded message.
    * @api private
    */
   JSONPPolling.prototype.post =  function  (data) {
     var  self =  this
       , query = io.util.query(
              this .socket.options.query
           't=' + (+ new  Date) +  '&i='  this .index
         );
     if  (! this .form) {
       var  form = document.createElement( 'form' )
         , area = document.createElement( 'textarea' )
         , id =  this .iframeId =  'socketio_iframe_'  this .index
         , iframe;
       form.className =  'socketio' ;
       form.style.position =  'absolute' ;
       form.style.top =  '0px' ;
       form.style.left =  '0px' ;
       form.style.display =  'none' ;
       form.target = id;
       form.method =  'POST' ;
       form.setAttribute( 'accept-charset' 'utf-8' );
       area.name =  'd' ;
       form.appendChild(area);
       document.body.appendChild(form);
       this .form = form;
       this .area = area;
     }
     this .form.action =  this .prepareUrl() + query;
     function  complete () {
       initIframe();
       self.socket.setBuffer( false );
     };
     function  initIframe () {
       if  (self.iframe) {
         self.form.removeChild(self.iframe);
       }
       try  {
         // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
         iframe = document.createElement( '<iframe name="' + self.iframeId + '">' );
       catch  (e) {
         iframe = document.createElement( 'iframe' );
         iframe.name = self.iframeId;
       }
       iframe.id = self.iframeId;
       self.form.appendChild(iframe);
       self.iframe = iframe;
     };
     initIframe();
     // we temporarily stringify until we figure out how to prevent
     // browsers from turning `\n` into `\r\n` in form inputs
     this .area.value = io.JSON.stringify(data);
     try  {
       this .form.submit();
     catch (e) {}
     if  ( this .iframe.attachEvent) {
       iframe.onreadystatechange =  function  () {
         if  (self.iframe.readyState ==  'complete' ) {
           complete();
         }
       };
     else  {
       this .iframe.onload = complete;
     }
     this .socket.setBuffer( true );
   };
   
   /**
    * Creates a new JSONP poll that can be used to listen
    * for messages from the Socket.IO server.
    *
    * @api private
    */
   JSONPPolling.prototype.get =  function  () {
     var  self =  this
       , script = document.createElement( 'script' )
       , query = io.util.query(
              this .socket.options.query
           't=' + (+ new  Date) +  '&i='  this .index
         );
     if  ( this .script) {
       this .script.parentNode.removeChild( this .script);
       this .script =  null ;
     }
     script.async =  true ;
     script.src =  this .prepareUrl() + query;
     script.onerror =  function  () {
       self.onClose();
     };
     var  insertAt = document.getElementsByTagName( 'script' )[0]
     insertAt.parentNode.insertBefore(script, insertAt);
     this .script = script;
     if  (indicator) {
       setTimeout( function  () {
         var  iframe = document.createElement( 'iframe' );
         document.body.appendChild(iframe);
         document.body.removeChild(iframe);
       }, 100);
     }
   };
   /**
    * Callback function for the incoming message stream from the Socket.IO server.
    *
    * @param {String} data The message
    * @api private
    */
   JSONPPolling.prototype._ =  function  (msg) {
     this .onData(msg);
     if  ( this .open) {
       this .get();
     }
     return  this ;
   };
   /**
    * The indicator hack only works after onload
    *
    * @param {Socket} socket The socket instance that needs a transport
    * @param {Function} fn The callback
    * @api private
    */
   JSONPPolling.prototype.ready =  function  (socket, fn) {
     var  self =  this ;
     if  (!indicator)  return  fn.call( this );
     io.util.load( function  () {
       fn.call(self);
     });
   };
   /**
    * Checks if browser supports this transport.
    *
    * @return {Boolean}
    * @api public
    */
   JSONPPolling.check =  function  () {
     return  'document'  in  global;
   };
   /**
    * Check if cross domain requests are supported
    *
    * @returns {Boolean}
    * @api public
    */
   JSONPPolling.xdomainCheck =  function  () {
     return  true ;
   };
   /**
    * Add the transport to your public io.transports array.
    *
    * @api private
    */
   io.transports.push( 'jsonp-polling' );
})(
     'undefined'  !=  typeof  io ? io.Transport : module.exports
   'undefined'  !=  typeof  io ? io : module.parent.exports
   this
);

 

 

 













本文转自朱先忠老师51CTO博客,原文链接: http://blog.51cto.com/zhuxianzhong/1616456,如需转载请自行联系原作者





相关文章
|
4月前
|
JavaScript 前端开发
js 匿名函数 中无法正常进行 异常捕获
js 匿名函数 中无法正常进行 异常捕获
28 1
|
8月前
|
JavaScript 前端开发
认识JavaScript中的闭包和匿名函数
认识JavaScript中的闭包和匿名函数
53 0
|
11月前
|
前端开发 JavaScript Serverless
前端祖传三件套JavaScript的函数之匿名函数
在JavaScript中,函数是实现各种功能和业务逻辑的基本手段。除了常规的函数声明和函数表达式外,JavaScript还支持匿名函数的定义方式。在这篇文章中,我们将介绍JavaScript中的匿名函数,以及其使用方法和注意事项
76 0
|
JavaScript 前端开发
JavaScript函数篇之ES6箭头函数与匿名函数
对于箭头函数,this 关键字始终表示定义箭头函数的对象。
116 0
|
JavaScript 前端开发
js执行机制(同步,异步)
js执行机制(同步,异步)
js执行机制(同步,异步)
|
前端开发 算法 JavaScript
LeetCode仅执行一次字符串交换能否使两个字符串相等使用JavaScript解题|前端学算法
LeetCode仅执行一次字符串交换能否使两个字符串相等使用JavaScript解题|前端学算法
103 0
LeetCode仅执行一次字符串交换能否使两个字符串相等使用JavaScript解题|前端学算法
|
JavaScript
JS 函数的执行时机
JS 函数的执行时机
64 0
|
JavaScript 前端开发 测试技术
软件测试|selenium执行js脚本
软件测试|selenium执行js脚本
145 0
软件测试|selenium执行js脚本
|
JavaScript 开发者
JS 函数的执行时机
JS 函数的执行时机
|
JavaScript
JS 函数的执行时机
JS 函数的执行时机
70 0