<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      深入理解JavaScript系列(4):立即調用的函數(shù)表達式

      2011-12-31 09:45  湯姆大叔  閱讀(117522)  評論(49)    收藏  舉報

      前言

      大家學JavaScript的時候,經常遇到自執(zhí)行匿名函數(shù)的代碼,今天我們主要就來想想說一下自執(zhí)行。

      在詳細了解這個之前,我們來談了解一下“自執(zhí)行”這個叫法,本文對這個功能的叫法也不一定完全對,主要是看個人如何理解,因為有的人說立即調用,有的人說自動執(zhí)行,所以你完全可以按照你自己的理解來取一個名字,不過我聽很多人都叫它為“自執(zhí)行”,但作者后面說了很多,來說服大家稱呼為“立即調用的函數(shù)表達式”。

      本文英文原文地址:http://benalman.com/news/2010/11/immediately-invoked-function-expression/

      什么是自執(zhí)行?

      在JavaScript里,任何function在執(zhí)行的時候都會創(chuàng)建一個執(zhí)行上下文,因為為function聲明的變量和function有可能只在該function內部,這個上下文,在調用function的時候,提供了一種簡單的方式來創(chuàng)建自由變量或私有子function。

      // 由于該function里返回了另外一個function,其中這個function可以訪問自由變量i
      //
      所有說,這個內部的function實際上是有權限可以調用內部的對象。

      function makeCounter() {
      // 只能在makeCounter內部訪問i
      var i = 0;

      return function () {
      console.log(++i);
      };
      }

      // 注意,counter和counter2是不同的實例,分別有自己范圍內的i。

      var counter = makeCounter();
      counter(); // logs: 1
      counter(); // logs: 2

      var counter2 = makeCounter();
      counter2(); // logs: 1
      counter2(); // logs: 2

      alert(i); // 引用錯誤:i沒有defind(因為i是存在于makeCounter內部)。

      很多情況下,我們不需要makeCounter多個實例,甚至某些case下,我們也不需要顯示的返回值,OK,往下看。

       

      問題的核心

      當你聲明類似function foo(){}或var foo = function(){}函數(shù)的時候,通過在后面加個括弧就可以實現(xiàn)自執(zhí)行,例如foo(),看代碼:

      // 因為想下面第一個聲明的function可以在后面加一個括弧()就可以自己執(zhí)行了,比如foo(),
      //
      因為foo僅僅是function() { /* code */ }這個表達式的一個引用

      var foo = function(){ /* code */ }

      // ...是不是意味著后面加個括弧都可以自動執(zhí)行?

      function(){ /* code */ }(); // SyntaxError: Unexpected token (
      //

      上述代碼,如果甚至運行,第2個代碼會出錯,因為在解析器解析全局的function或者function內部function關鍵字的時候,默認是認為function聲明,而不是function表達式,如果你不顯示告訴編譯器,它默認會聲明成一個缺少名字的function,并且拋出一個語法錯誤信息,因為function聲明需要一個名字。

      旁白:函數(shù)(function),括弧(paren),語法錯誤(SyntaxError)

      有趣的是,即便你為上面那個錯誤的代碼加上一個名字,他也會提示語法錯誤,只不過和上面的原因不一樣。在一個表達式后面加上括號(),該表達式會立即執(zhí)行,但是在一個語句后面加上括號(),是完全不一樣的意思,他的只是分組操作符。

      // 下面這個function在語法上是沒問題的,但是依然只是一個語句
      //
      加上括號()以后依然會報錯,因為分組操作符需要包含表達式

      function foo(){ /* code */ }(); // SyntaxError: Unexpected token )

      // 但是如果你在括弧()里傳入一個表達式,將不會有異常拋出
      //
      但是foo函數(shù)依然不會執(zhí)行
      function foo(){ /* code */ }( 1 );

      // 因為它完全等價于下面這個代碼,一個function聲明后面,又聲明了一個毫無關系的表達式:
      function foo(){ /* code */ }

      ( 1 );

      你可以訪問ECMA-262-3 in detail. Chapter 5. Functions 獲取進一步的信息。

      自執(zhí)行函數(shù)表達式

      要解決上述問題,非常簡單,我們只需要用大括弧將代碼的代碼全部括住就行了,因為JavaScript里括弧()里面不能包含語句,所以在這一點上,解析器在解析function關鍵字的時候,會將相應的代碼解析成function表達式,而不是function聲明。

      // 下面2個括弧()都會立即執(zhí)行

      (function () { /* code */ } ()); // 推薦使用這個
      (function () { /* code */ })(); // 但是這個也是可以用的

      // 由于括弧()和JS的&&,異或,逗號等操作符是在函數(shù)表達式和函數(shù)聲明上消除歧義的
      //
      所以一旦解析器知道其中一個已經是表達式了,其它的也都默認為表達式了
      //
      不過,請注意下一章節(jié)的內容解釋

      var i = function () { return 10; } ();
      true && function () { /* code */ } ();
      0, function () { /* code */ } ();

      // 如果你不在意返回值,或者不怕難以閱讀
      //
      你甚至可以在function前面加一元操作符號

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

      // 還有一個情況,使用new關鍵字,也可以用,但我不確定它的效率
      //
      http://twitter.com/kuvos/status/18209252090847232

      new function () { /* code */ }
      new function () { /* code */ } () // 如果需要傳遞參數(shù),只需要加上括弧()

      上面所說的括弧是消除歧義的,其實壓根就沒必要,因為括弧本來內部本來期望的就是函數(shù)表達式,但是我們依然用它,主要是為了方便開發(fā)人員閱讀,當你讓這些已經自動執(zhí)行的表達式賦值給一個變量的時候,我們看到開頭有括弧(,很快就能明白,而不需要將代碼拉到最后看看到底有沒有加括弧。

      用閉包保存狀態(tài)

      和普通function執(zhí)行的時候傳參數(shù)一樣,自執(zhí)行的函數(shù)表達式也可以這么傳參,因為閉包直接可以引用傳入的這些參數(shù),利用這些被lock住的傳入參數(shù),自執(zhí)行函數(shù)表達式可以有效地保存狀態(tài)。

      // 這個代碼是錯誤的,因為變量i從來就沒背locked住
      //
      相反,當循環(huán)執(zhí)行以后,我們在點擊的時候i才獲得數(shù)值
      //
      因為這個時候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');

      }

      // 這個是可以用的,因為他在自執(zhí)行函數(shù)表達式閉包內部
      //
      i的值作為locked的索引存在,在循環(huán)執(zhí)行結束以后,盡管最后i的值變成了a元素總數(shù)(例如10)
      //
      但閉包內部的lockedInIndex值是沒有改變,因為他已經執(zhí)行完畢了
      //
      所以當點擊連接的時候,結果是正確的

      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);

      }

      // 你也可以像下面這樣應用,在處理函數(shù)那里使用自執(zhí)行函數(shù)表達式
      //
      而不是在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不在一個作用于,所以不會出現(xiàn)問題,這也是匿名函數(shù)+閉包的威力。

      自執(zhí)行匿名函數(shù)和立即執(zhí)行的函數(shù)表達式區(qū)別

      在這篇帖子里,我們一直叫自執(zhí)行函數(shù),確切的說是自執(zhí)行匿名函數(shù)(Self-executing anonymous function),但英文原文作者一直倡議使用立即調用的函數(shù)表達式(Immediately-Invoked Function Expression)這一名稱,作者又舉了一堆例子來解釋,好吧,我們來看看:

      // 這是一個自執(zhí)行的函數(shù),函數(shù)內部執(zhí)行自身,遞歸
      function foo() { foo(); }

      // 這是一個自執(zhí)行的匿名函數(shù),因為沒有標示名稱
      //
      必須使用arguments.callee屬性來執(zhí)行自己
      var foo = function () { arguments.callee(); };

      // 這可能也是一個自執(zhí)行的匿名函數(shù),僅僅是foo標示名稱引用它自身
      //
      如果你將foo改變成其它的,你將得到一個used-to-self-execute匿名函數(shù)
      var foo = function () { foo(); };

      // 有些人叫這個是自執(zhí)行的匿名函數(shù)(即便它不是),因為它沒有調用自身,它只是立即執(zhí)行而已。
      (function () { /* code */ } ());

      // 為函數(shù)表達式添加一個標示名稱,可以方便Debug
      //
      但一定命名了,這個函數(shù)就不再是匿名的了
      (function foo() { /* code */ } ());

      // 立即調用的函數(shù)表達式(IIFE)也可以自執(zhí)行,不過可能不常用罷了
      (function () { arguments.callee(); } ());
      (function foo() { foo(); } ());

      // 另外,下面的代碼在黑莓5里執(zhí)行會出錯,因為在一個命名的函數(shù)表達式里,他的名稱是undefined
      //
      呵呵,奇怪
      (function foo() { foo(); } ());

      希望這里的一些例子,可以讓大家明白,什么叫自執(zhí)行,什么叫立即調用。

      注:arguments.callee在ECMAScript 5 strict mode里被廢棄了,所以在這個模式下,其實是不能用的。

      最后的旁白:Module模式

      在講到這個立即調用的函數(shù)表達式的時候,我又想起來了Module模式,如果你還不熟悉這個模式,我們先來看看代碼:

      // 創(chuàng)建一個立即調用的匿名函數(shù)表達式
      //
      return一個變量,其中這個變量里包含你要暴露的東西
      //
      返回的這個變量將賦值給counter,而不是外面聲明的function自身

      var counter = (function () {
      var i = 0;

      return {
      get: function () {
      return i;
      },
      set: function (val) {
      i = val;
      },
      increment: function () {
      return ++i;
      }
      };
      } ());

      // counter是一個帶有多個屬性的對象,上面的代碼對于屬性的體現(xiàn)其實是方法

      counter.get(); // 0
      counter.set(3);
      counter.increment(); // 4
      counter.increment(); // 5

      counter.i; // undefined 因為i不是返回對象的屬性
      i; // 引用錯誤: i 沒有定義(因為i只存在于閉包)

      關于更多Module模式的介紹,請訪問我的上一篇帖子:深入理解JavaScript系列(2):全面解析Module模式 。

      更多閱讀

      希望上面的一些例子,能讓你對立即調用的函數(shù)表達(也就是我們所說的自執(zhí)行函數(shù))有所了解,如果你想了解更多關于function和Module模式的信息,請繼續(xù)訪問下面列出的網站:

      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系列

      深入理解JavaScript系列文章,包括了原創(chuàng),翻譯,轉載等各類型的文章,如果對你有用,請推薦支持一把,給大叔寫作的動力。

      主站蜘蛛池模板: 精品无码人妻一区二区三区| 西西人体大胆444WWW| 极品尤物一区二区三区| 国产mv在线天堂mv免费观看| 一道本AV免费不卡播放| 国产999久久高清免费观看| 麻豆精品一区二正一三区| 日韩精品18禁一区二区| 国产亚洲精品视频一二区| 99久久精品国产一区二区暴力| 国产福利姬喷水福利在线观看| 黑人欧美一级在线视频| 富川| 在线精品国产中文字幕| 在线国产极品尤物你懂的| 日区中文字幕一区二区| 又大又粗又硬又爽黄毛少妇 | 亚洲精品成人区在线观看| 99久久久国产精品免费无卡顿| 人妻另类 专区 欧美 制服| 成年无码av片在线蜜芽| 久久亚洲国产欧洲精品一| 久久精品国产99久久无毒不卡| 午夜福利精品国产二区| 成人无码区在线观看| 人成午夜免费大片| 欧美xxxx做受欧美.88| 亚洲高清WWW色好看美女| 国产伦码精品一区二区| 亚洲成人动漫av在线| 九九色这里只有精品国产| 一本一道av无码中文字幕麻豆| 精品人妻大屁股白浆无码| 女子spa高潮呻吟抽搐| 国内精品久久久久影院网站| 欧美寡妇xxxx黑人猛交| 亚洲欧美成人综合久久久| 免费AV片在线观看网址| 色综合色综合久久综合频道88| 人人澡人人透人人爽| 黄页网站在线观看免费视频|