1. 程式人生 > >立即執行函式(IIFE)的理解與運用

立即執行函式(IIFE)的理解與運用

作為JavaScript的常用語法,立即執行函式IIFE(Immediately-Invoked Function Expression)是值得我們認真去學習探究的。

一、建立函式的兩種方式

我們先從基礎講起,要建立一個JS函式,有兩種方式。

(一)函式定義(Function Declaration)

function Identifier ( Parameters ){ FunctionBody }

函式定義中,引數(Parameters)識別符號(Identifier )是必不可少的。如果遺漏,會報提示如下錯誤:Expected identifier

(二)函式表示式(Function Expression)

function Identifier(Parameters){ FunctionBody }

函式表示式中,引數和識別符號都是可選的。

那麼我們是不是可以通過有無識別符號來判斷,建立函式的是一個函式定義,還是一個函式表示式呢?很遺憾,不行!

我們剛剛說過,函式表示式中識別符號是可選的。那如果函式表示式有識別符號,又該如何判斷?

其實,"function Identifier(Parameters){ FunctionBody }"並不是一個完整的函式表示式,完整的函式的表示式,需要一個賦值操作。

比如: var name=function Identifier(Parameters){ FunctionBody }

這裡的Identifier常常被省略,至於為什麼,由於該主題內容涉及面較廣,在此文中不作討論。

好了。兩種建立函式的方式例項如下:

//function expressionvar myExpression = function () { return window.location }//function declarationfunction myDeclaration() { return window.location.hostname }

二、立即執行函式

顧名思義,立即執行函式可以讓你的函式在建立後立即執行。

(一)基本結構

這樣的函式有多常用呢,我們可以看看下面的程式碼:

(function( window, undefined ) {//……})(window);

這段程式碼,大家一定不會陌生。是的,它就是我們"Write less, do more"的jQuery。

jQuery整個檔案就是一個立即執行函式。

(function(){})();  是立即執行函式常用的表現形式之一。

另一種也很常用:

(function(){}());

以上兩種是比較常用的寫法,但立即執行函式的寫法因人而異。記住以下兩點就可以了。

如果是函式表示式,可直接在其後加"()"立即執行。

如果是函式宣告,可以通過"()"、"+"、"-"、"void"、"new"等運算子將其轉換為函式表示式,然後再加"()"立即執行。

比如,下面的寫法也是沒有問題的。

void function(){}(alert("ok"));

在執行前,可以在最後呼叫的"()"傳入我們需要的引數,比如jQuery就把window物件作為實參傳入了立即函式內部。

(二)使用時機

什麼時候需要用到立即執行函式呢?

1.當我們需要寫一個js檔案,並且複用率很高的時候,建議使用。

2.如果宣告的函式只需要呼叫一次,建議使用。

3.獨立模組,這個和第一點差不多。單獨提出來,是想強調一下立即執行函式的好處,開發時,它能做到各模組的低耦合,減少對全域性作用域的汙染。

(三)例項及好處

無例項,無真相。找什麼例項好呢?還是我們的jQuery吧。

   1 /*!   2  * jQuery JavaScript Library v1.4.4   3  * http://jquery.com/   4  *   5  * Copyright 2010, John Resig   6  * Dual licensed under the MIT or GPL Version 2 licenses.   7  * http://jquery.org/license   8  *   9  * Includes Sizzle.js  10  * http://sizzlejs.com/  11  * Copyright 2010, The Dojo Foundation  12  * Released under the MIT, BSD, and GPL Licenses.  13  *  14  * Date: Thu Nov 11 19:04:53 2010 -0500  15  */  16 (function( window, undefined ) {  17   18 // Use the correct document accordingly with window argument (sandbox)  19 var document = window.document;  20 var jQuery = (function() {  21   22 // Define a local copy of jQuery  23 var jQuery = function( selector, context ) {  24         // The jQuery object is actually just the init constructor 'enhanced'  25         return new jQuery.fn.init( selector, context );  26     },  27   28     // Map over jQuery in case of overwrite  29     _jQuery = window.jQuery,  30   31     // Map over the $ in case of overwrite  32     _$ = window.$,  33   34     // A central reference to the root jQuery(document)  35     rootjQuery,  36   37     // A simple way to check for HTML strings or ID strings  38     // (both of which we optimize for)  39     quickExpr = /^(?:[^<]*(<[/w/W]+>)[^>]*$|#([/w/-]+)$)/,  40   41     // Is it a simple selector  42     isSimple = /^.[^:#/[/.,]*$/,  43   44     // Check if a string has a non-whitespace character in it  45     rnotwhite = //S/,  46     rwhite = //s/,  47   48     // Used for trimming whitespace  49     trimLeft = /^/s+/,  50     trimRight = //s+$/,  51   52     // Check for non-word characters  53     rnonword = //W/,  54   55     // Check for digits  56     rdigit = //d/,  57   58     // Match a standalone tag  59     rsingleTag = /^<(/w+)/s*//?>(?:<///1>)?$/,  60   61     // JSON RegExp  62     rvalidchars = /^[/],:{}/s]*$/,  63     rvalidescape = ///(?:["////bfnrt]|u[0-9a-fA-F]{4})/g,  64     rvalidtokens = /"[^"///n/r]*"|true|false|null|-?/d+(?:/./d*)?(?:[eE][+/-]?/d+)?/g,  65     rvalidbraces = /(?:^|:|,)(?:/s*/[)+/g,  66   67     // Useragent RegExp  68     rwebkit = /(webkit)[ //]([/w.]+)/,  69     ropera = /(opera)(?:.*version)?[ //]([/w.]+)/,  70     rmsie = /(msie) ([/w.]+)/,  71     rmozilla = /(mozilla)(?:.*? rv:([/w.]+))?/,  72   73     // Keep a UserAgent string for use with jQuery.browser  74     userAgent = navigator.userAgent,  75   76     // For matching the engine and version of the browser  77     browserMatch,  78       79     // Has the ready events already been bound?  80     readyBound = false,  81       82     // The functions to execute on DOM ready  83     readyList = [],  84   85     // The ready event handler  86     DOMContentLoaded,  87   88     // Save a reference to some core methods  89     toString = Object.prototype.toString,  90     hasOwn = Object.prototype.hasOwnProperty,  91     push = Array.prototype.push,  92     slice = Array.prototype.slice,  93     trim = String.prototype.trim,  94     indexOf = Array.prototype.indexOf,  95       96     // [[Class]] -> type pairs  97     class2type = {};  98   99 jQuery.fn = jQuery.prototype = { 100     init: function( selector, context ) { 101         var match, elem, ret, doc; 102  103         // Handle $(""), $(null), or $(undefined) 104         if ( !selector ) { 105             return this; 106         } 107  108         // Handle $(DOMElement) 109         if ( selector.nodeType ) { 110             this.context = this[0] = selector; 111             this.length = 1; 112             return this; 113         } 114          115         // The body element only exists once, optimize finding it 116         if ( selector === "body" && !context && document.body ) { 117             this.context = document; 118             this[0] = document.body; 119             this.selector = "body"; 120             this.length = 1; 121             return this; 122         } 123  124         // Handle HTML strings 125         if ( typeof selector === "string" ) { 126             // Are we dealing with HTML string or an ID? 127             match = quickExpr.exec( selector ); 128  129             // Verify a match, and that no context was specified for #id 130             if ( match && (match[1] || !context) ) { 131  132                 // HANDLE: $(html) -> $(array) 133                 if ( match[1] ) { 134                     doc = (context ? context.ownerDocument || context : document); 135  136                     // If a single string is passed in and it's a single tag 137                     // just do a createElement and skip the rest 138                     ret = rsingleTag.exec( selector ); 139  140                     if ( ret ) { 141                         if ( jQuery.isPlainObject( context ) ) { 142                             selector = [ document.createElement( ret[1] ) ]; 143                             jQuery.fn.attr.call( selector, context, true ); 144  145                         } else { 146                             selector = [ doc.createElement( ret[1] ) ]; 147                         } 148  149                     } else { 150                         ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); 151                         selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; 152                     } 153                      154                     return jQuery.merge( this, selector ); 155                      156                 // HANDLE: $("#id") 157                 } else { 158                     elem = document.getElementById( match[2] ); 159  160                     // Check parentNode to catch when Blackberry 4.6 returns 161                     // nodes that are no longer in the document #6963 162                     if ( elem && elem.parentNode ) { 163                         // Handle the case where IE and Opera return items 164                         // by name instead of ID 165                         if ( elem.id !== match[2] ) { 166                             return rootjQuery.find( selector ); 167                         } 168  169                         // Otherwise, we inject the element directly into the jQuery object 170                         this.length = 1; 171                         this[0] = elem; 172                     } 173  174                     this.context = document; 175                     this.selector = selector; 176                     return this; 177                 } 178  179             // HANDLE: $("TAG") 180             } else if ( !context && !rnonword.test( selector ) ) { 181                 this.selector = selector; 182                 this.context = document; 183                 selector = document.getElementsByTagName( selector ); 184                 return jQuery.merge( this, selector ); 185  186             // HANDLE: $(expr, $(...)) 187             } else if ( !context || context.jquery ) { 188                 return (context || rootjQuery).find( selector ); 189  190             // HANDLE: $(expr, context) 191             // (which is just equivalent to: $(context).find(expr) 192             } else { 193                 return jQuery( context ).find( selector ); 194             } 195  196         // HANDLE: $(function) 197         // Shortcut for document ready 198         } else if ( jQuery.isFunction( selector ) ) { 199             return rootjQuery.ready( selector ); 200         } 201  202         if (selector.selector !== undefined) { 203             this.selector = selector.selector; 204             this.context = selector.context; 205         } 206  207         return jQuery.makeArray( selector, this ); 208     }, 209  210     // Start with an empty selector 211     selector: "", 212  213     // The current version of jQuery being used 214     jquery: "1.4.4", 215  216     // The default length of a jQuery object is 0 217     length: 0, 218  219     // The number of elements contained in the matched element set 220     size: function() { 221         return this.length; 222     }, 223  224     toArray: function() { 225         return slice.call( this, 0 ); 226     }, 227  228     // Get the Nth element in the matched element set OR 229     // Get the whole matched element set as a clean array 230     get: function( num ) { 231         return num == null ? 232  233             // Return a 'clean' array 234             this.toArray() : 235  236             // Return just the object 237             ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); 238     }, 239  240     // Take an array of elements and push it onto the stack 241     // (returning the new matched element set) 242     pushStack: function( elems, name, selector ) { 243         // Build a new jQuery matched element set 244         var ret = jQuery(); 245  246         if ( jQuery.isArray( elems ) ) { 247             push.apply( ret, elems ); 248          249         } else { 250             jQuery.merge( ret, elems ); 251         } 252  253         // Add the old object onto the stack (as a reference) 254         ret.prevObject = this; 255  256         ret.context = this.context; 257  258         if ( name === "find" ) { 259             ret.selector = this.selector + (this.selector ? " " : "") + selector; 260         } else if ( name ) { 261             ret.selector = this.selector + "." + name + "(" + selector + ")"; 262         } 263  264         // Return the newly-formed element set 265         return ret; 266     }, 267  268     // Execute a callback for every element in the matched set. 269     // (You can seed the arguments with an array of args, but this is 270     // only used internally.) 271     each: function( callback, args ) { 272         return jQuery.each( this, callback, args ); 273     }, 274      275     ready: function( fn ) { 276         // Attach the listeners 277         jQuery.bindReady(); 278  279         // If the DOM is already ready 280         if ( jQuery.isReady ) { 281             // Execute the function immediately 282             fn.call( document, jQuery ); 283  284         // Otherwise, remember the function for later 285         } else if ( readyList ) { 286             // Add the function to the wait list 287             readyList.push( fn ); 288         } 289  290         return this; 291     }, 292      293     eq: function( i ) { 294         return i === -1 ? 295             this.slice( i ) : 296             this.slice( i, +i + 1 ); 297     }, 298  299     first: function() { 300         return this.eq( 0 ); 301     }, 302  303     last: function() { 304         return this.eq( -1 ); 305     }, 306  307     slice: function() { 308         return this.pushStack( slice.apply( this, arguments ), 309             "slice", slice.call(arguments).join(",") ); 310     }, 311  312     map: function( callback ) { 313         return this.pushStack( jQuery.map(this, function( elem, i ) { 314             return callback.call( elem, i, elem ); 315         })); 316     }, 317      318     end: function() { 319         return this.prevObject || jQuery(null); 320     }, 321  322     // For internal use only. 323     // Behaves like an Array's method, not like a jQuery method. 324     push: push, 325     sort: [].sort, 326     splice: [].splice 327 }; 328  329 // Give the init function the jQuery prototype for later instantiation 330 jQuery.fn.init.prototype = jQuery.fn; 331  332 jQuery.extend = jQuery.fn.extend = function() { 333      var options, name, src, copy, copyIsArray, clone, 334         target = arguments[0] || {}, 335         i = 1, 336         length = arguments.length, 337         deep = false; 338  339     // Handle a deep copy situation 340     if ( typeof target === "boolean" ) { 341         deep = target; 342         target = arguments[1] || {}; 343         // skip the boolean and the target 344         i = 2; 345     } 346  347     // Handle case when target is a string or something (possible in deep copy) 348     if ( typeof target !== "object" && !jQuery.isFunction(target) ) { 349         target = {}; 350     } 351  352     // extend jQuery itself if only one argument is passed 353     if ( length === i ) { 354         target = this; 355         --i; 356     } 357  358     for ( ; i < length; i++ ) { 359         // Only deal with non-null/undefined values 360         if ( (options = arguments[ i ]) != null ) { 361             // Extend the base object 362             for ( name in options ) { 363                 src = target[ name ]; 364                 copy = options[ name ]; 365  366                 // Prevent never-ending loop 367                 if ( target === copy ) { 368                     continue; 369                 } 370  371                 // Recurse if we're merging plain objects or arrays 372                 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { 373                     if ( copyIsArray ) { 374                         copyIsArray = false; 375                         clone = src && jQuery.isArray(src) ? src : []; 376  377                     } else { 378                         clone = src && jQuery.isPlainObject(src) ? src : {}; 379                     } 380  381                     // Never move original objects, clone them 382                     target[ name ] = jQuery.extend( deep, clone, copy ); 383  384                 // Don't bring in undefined values 385                 } else if ( copy !== undefined ) { 386                     target[ name ] = copy; 387                 } 388             } 389         } 390     } 391  392     // Return the modified object 393     return target; 394 }; 395  396 jQuery.extend({ 397     noConflict: function( deep ) { 398         window.$ = _$; 399  400         if ( deep ) { 401             window.jQuery = _jQuery; 402         } 403  404         return jQuery; 405     }, 406      407     // Is the DOM ready to be used? Set to true once it occurs. 408     isReady: false, 409  410     // A counter to track how many items to wait for before 411