欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript 匿名函數(shù)和閉包介紹

 更新時間:2015年04月13日 12:14:28   投稿:mdxy-dxy  
這篇文章主要介紹了JavaScript 匿名函數(shù)和閉包介紹,需要的朋友可以參考下

匿名函數(shù):沒有名字的函數(shù);
閉包:可訪問一個函數(shù)作用域里的變量的函數(shù);

一 匿名函數(shù)

// 普通函數(shù)
  function box(){            // 函數(shù)名是box;
    return 'Lee';           
  }
  box();                // =>Lee; 調(diào)用函數(shù);
// 匿名函數(shù)
  function(){              // 匿名函數(shù),會報錯;
    return 'Lee';
  }
// 通過表達式自我執(zhí)行
  (function(name){
    console.log(name);        // =>Lee;
  })("Lee");              // "()"表示執(zhí)行函數(shù),并且可以傳參;
// 把匿名函數(shù)賦值給變量
  var box = function(){        // 將匿名函數(shù)賦給變量;
    return 'Lee';
  };
  console.log(box());         // 調(diào)用方式和函數(shù)調(diào)用相似;
// 函數(shù)里的匿名函數(shù)
  function box(){
    return function(name){      // 函數(shù)里的匿名函數(shù),產(chǎn)生閉包;
      return name;
    };
  };
  console.log(box()("Lee"));      // 函數(shù)box()調(diào)用匿名函數(shù),并傳參;

二 閉包

閉包:有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù);
創(chuàng)建閉包的常見方式:在一個函數(shù)內(nèi)部創(chuàng)建另一個函數(shù);通過另一個函數(shù)訪問這個函數(shù)的局部變量;

// 通過閉包可以返回局部變量
  function box(){
    var user = 'Lee';
    return function(){        // 通過匿名函數(shù)返回box()的局部變量user;
      return user;
    };
  }
  console.log(box()());        // =>Lee; 直接調(diào)用box()()來獲得匿名函數(shù)的返回值;

  var b = box();
  console.log(b());          // =>Lee; 另一種調(diào)用匿名函數(shù)方式;

// 優(yōu)點:可以把局部變量駐留在內(nèi)存中,可以避免使用全局變量; 
// (全局變量污染導致應用程序不可預測性,每個模塊都可調(diào)用必將引來災難;所以推薦使用私有的,封裝的局部變量);
// 缺點:
// 通過全局變量來累加
  var age = 100;            // 全局變量;
  function box(){
    age++;              // 模塊級可以調(diào)用全局變量,進行累加;
  };
  box();                // 執(zhí)行函數(shù),累加一次;
  console.log(age);           // =>101; 輸出全局變量;
  box();                // 執(zhí)行函數(shù),累加一次;
  console.log(age);           // =>102; 輸出全局變量;
// 通過局部變量無法實現(xiàn)累加
  function box(){
    var age = 100;
    age++;              // 實現(xiàn)累加;
    return age;
  }
  console.log(box());          // =>101;
  console.log(box());          // =>101; 無法實現(xiàn)累加,因為第二次調(diào)用函數(shù)時,函數(shù)內(nèi)部變量age又被初始化了;

// 通過閉包可以實現(xiàn)局部變量的累加
  function box(){
    var age = 100;
    return function(){        // 匿名函數(shù)內(nèi)實現(xiàn)累加;
      age++;
      return age;          // 并返回累加后的變量; 
    };                // 此時box()函數(shù)的局部變量age的值已經(jīng)被修改為累加后的值;
  }
  var b = box();            // 給box()函數(shù)賦值給變量;
  console.log(b());           // =>101; 調(diào)用匿名函數(shù),累加一次;
  console.log(b());           // =>102; 第二次調(diào)用匿名函數(shù),累加兩次;

// PS:由于閉包里作用域返回的局部變量資源不會被立刻銷毀回收,所以可能會占用更多的內(nèi)存;所以過度使用閉包會導致性能下降;(將閉包引用在"私有作用域"中即可實現(xiàn)變量銷毀)
// 作用域鏈的機制導致一個問題,在循環(huán)中里的匿名函數(shù)取得的任何變量都是最后一個值; ?
// 循環(huán)里包含匿名函數(shù)
  function box(){
    var arr = [];
    for(var i=0; i<5; i++){     // 當聲明變量i=5時,循環(huán)停止;而此時循環(huán)里的變量i==5; 
      arr[i] = function(){    // arr[i]得到的只是沒有執(zhí)行的匿名函數(shù)function(){};
        return i;        
      };
    };
    return arr;           // arr = [function,function,function,function,function];
  }
  var b = box();           // =>[function,function,function,function,function]; 得到函數(shù)box()返回的數(shù)組arr;
  console.log(b.length);       // =>5; 得到函數(shù)集合數(shù)組長度;
  for(var i=0; i<b.length; i++){
    console.log(box()[i]());    // =>5,5,5,5,5; 輸出每個函數(shù)的值,都是最后一個值;
  }
  // 上面的例子輸出的結(jié)果都是5,也就是循環(huán)后得到的最大i值;
  // 因為b[i]調(diào)用的是匿名函數(shù),匿名函數(shù)并沒有自我執(zhí)行,等到調(diào)用的時候,box()已執(zhí)行完畢,i早已變成5;

// 循環(huán)里包含匿名函數(shù)-改1,自我執(zhí)行匿名函數(shù)
  function box(){
    var arr = [];
    for(var i=0; i<5; i++){
      arr[i] = (function(num){  // arr[i]得到的是匿名函數(shù)執(zhí)行后的結(jié)果數(shù)值0-4;
        return num; 
      })(i);           // 自我執(zhí)行并傳參;
    }
    return arr; 
  }
  var b = box();           // =>[0,1,2,3,4]; 此時b代表box()返回的數(shù)組;
  for (var i = 0; i < b.length; i++) {
    console.log(b[i]);       // 0 1 2 3 4; 這里返回的是數(shù)值;
  };
  // 例子中,我們讓匿名函數(shù)進行自我執(zhí)行,導致最終返回給a[i]的是數(shù)組而不是函數(shù)了;最終導致b[0]-b[4]中保留了0,1,2,3,4的值;

// 循環(huán)里包含匿名函數(shù)-改2,匿名函數(shù)里再做個匿名函數(shù);
  function box(){
    var arr = []; 
    for(var i=0; i<5; i++){
      arr[i] = (function(num){
        return function(){   // 返回函數(shù);
          return num;      
        }
      })(i);
    }
    return arr;           // arr = [function,function,function,function,function];
  }
  var b = box();
  for (var i = 0; i < b.length; i++) {
    console.log(b[i]());      // 0,1,2,3,4; 
  };

// 改1和改2中,我們通過匿名函數(shù)自我執(zhí)行,立即把結(jié)果賦值給arr[i];
// 每一個i,是調(diào)用方通過按值傳遞的,所以最終返回的都是指定的遞增的i;而不是box()函數(shù)中的變量i;

三 this對象

// 在閉包中使用this對象可能會導致一些問題;this對象是在運行時基于函數(shù)的執(zhí)行環(huán)境綁定的;
// 如果this在全局范圍就是指向window,如果在對象內(nèi)部就指向這個對象;
// 而閉包卻在運行時指向window的,因為閉包并不屬于這個對象的屬性或方法; 
  var user = 'Window';
  var obj = {
    user:'Object',
    getUserFunction:function(){
      return function(){            // 閉包不屬于obj,里面的this指向window;
        return this.user;
      };
    }
  };
  console.log(obj.getUserFunction()());      // =>Window;

  // 可以強制指向某個對象
  console.log(obj.getUserFunction().call(obj));  // =>Object;

  // 也可以從上一個作用域中的得到對象
  getUserFunction:function(){
    var that = this;               // 從對象的方法里得到this;此時that指向obj對象;
    return function(){
      return that.user;
    }
  }
  console.log(obj.getUserFunction()());      // =>Object;

四 內(nèi)存泄漏

// 由于IE的JScript對象和DOM對象使用不同的垃圾收集方式,因此閉包在IE中會導致內(nèi)存泄漏問題,也就是無法銷毀駐留在內(nèi)存中的元素;
  function box(){
    var oDiv = document.getElementById('oDiv'); // oDiv用完之后一直駐留在內(nèi)存中;
    oDiv.onclick = function(){
      alert(oDiv.innerHTML);          // 這里用oDiv導致內(nèi)存泄漏;
    };
    oDiv = null;                 // 解除引用;
  }
  box();
  // 由于匿名函數(shù)保存了一個對box()的活動對象的引用,因此就會導致無法減少oDiv的引用數(shù);
  // 只要匿名函數(shù)存在,oDiv的引用數(shù)至少也是1;因此它所占用的內(nèi)存就永遠不會被回收;
  // PS:如果沒有使用解除引用,那么要等到瀏覽器關閉才得以釋放;

五 模仿塊級作用域(定義并立即調(diào)用一個匿名函數(shù))

// JS沒有塊級作用域的概念;
// 這意味著在塊語句(for語句/if語句)中定義的變量,實際上是在包含函數(shù)中而非語句中創(chuàng)建的;
  function box(count){
    for(var i=0; i<count; i++){}        // box(2); => count=2; i=2時循環(huán)停止,此時i=2;
    console.log(i);               // =>2; i不會因為離開了for塊就失效;
  }
  box(2);

  function box(count){
    for(var i=0; i<count; i++){}
    var i;                   // 就算重新聲明,也不會覆蓋前面的值;
    console.log(i);
  }
  box(2);
// 在JavaScript中,變量i是定義在box()的活動對象中的,因此從它有定義開始,就可以在函數(shù)內(nèi)部隨處訪問它;
// 以上兩個例子,說明JavaScript沒有塊級語句的作用域,if(){}/for(){}等沒有作用域;
// 如果有作用域的話,出了這個范圍i就應該被銷毀;

// JavaScript不會提醒是否多次聲明了同一個變量;遇到這種情況,它只會對后續(xù)的聲明視而不見(如果是初始化并賦值,還是會執(zhí)行的);

// 模仿塊級作用域(私有作用域)
  (function(){
    // 這里是塊級作用域;
  })();
  // 以上代碼定義并立即調(diào)用了一個匿名函數(shù);將函數(shù)聲明包含在一對圓括號中,表示它實際上是一個函數(shù)表達式;

// 使用塊級作用域(私有作用域)改寫
  function box(count){
    (function(){
      for(var i=0; i<count; i++){}
    })();
    console.log(i);                // 報錯,無法訪問;變量i在私有作用域中,出了私有作用域即被銷毀了.
  }
  box(2);
// 使用了塊級作用域后,匿名函數(shù)中定義的任何變量,都會在執(zhí)行結(jié)束時被銷毀;(i只能在循環(huán)中使用,使用后即被銷毀);
// 而私有作用域中能夠訪問變量count,是因為這個匿名函數(shù)是一個閉包,他能夠訪問包含作用域中的所有變量;
// 這種技術經(jīng)常在全局作用域中被用在函數(shù)外部,從而限制向全局作用域中添加過多的變量和函數(shù);
// 一般來說,我們都應該盡可能少向全局作用域中添加變量和函數(shù);過多的全局變量和函數(shù)很容易導致命名沖突;
// 使用塊級作用域,每個開發(fā)者既可以使用自己的變量,又不必擔心搞亂全局作用域;
  (function(){
    var box = [1,2,3,4];
    console.log(box);              // =>[1,2,3,4]; box出來就不被認識了;
  })();                      // 銷毀匿名函數(shù)中的變量;
  console.log(box);                // =>box is not defined;
  // 在全局作用域中使用塊級作用域可以減少閉包占用的內(nèi)存問題;因為沒有指向匿名函數(shù)的引用
  // 只要函數(shù)執(zhí)行完畢,就可以立即銷毀其作用域鏈了;

六 私有變量

// JavaScript沒用私有屬性的概念;所有的屬性都是公用的;
// 不過有一個私有變量的概念:在任何函數(shù)中定義的變量,都可以認為是私有變量,因為不能在函數(shù)外部訪問這些變量;
// 私有變量包括函數(shù)的參數(shù)/局部變量和在函數(shù)內(nèi)部定義的其他函數(shù);
  function box(){
    var age = 100;                 // 私有變量,外部無法訪問;
  }

// 而通過內(nèi)部創(chuàng)建一個閉包,那么閉包通過自己的作用域鏈也可以訪問這些變量;
// 而利用這一點,可以創(chuàng)建用于訪問私有變量的公用方法;特權(quán)方法;
  function Box(){                  // 構(gòu)造函數(shù);
    var age = 100;                 // 私有變量;
    function run(){                // 私有函數(shù);
      return '運行中...';
    };
    this.get = function(){             // 對外公共的特權(quán)方法;
      return age+run();             // 將閉包賦值給變量;
    };
  }
  var box = new Box();
  console.log(box.get());

// 可以通過構(gòu)造方法傳參來訪問私有變量
  function Person(name){
    var user = name;              // 這句其實可以省略;
    this.getUser = function(){
      return user;
    };
    this.setUser = function(name){
      user = name;
    }
  }
  var p = new Person('Lee');
  console.log(p.getUser());            // =>Lee;
  console.log(p.setUser('Jack'));
  console.log(p.getUser());            // =>Jack;
  // 但是,構(gòu)造函數(shù)模式的缺點是針對每個實例都會創(chuàng)建同樣一組新方法;而使用靜態(tài)私有變量來實現(xiàn)特權(quán)方法就可以避免這個問題;

七 靜態(tài)私有變量

// 通過塊級作用域(私有作用域)中定義私有變量或函數(shù),同樣可以創(chuàng)建對外公共的特權(quán)方法;
  (function(){                  // 創(chuàng)建私有作用域;
    var age = 100;               // 靜態(tài)私有變量;
    function run(){
      return '運行中...';
    };
    Box = function(){};             // 使用函數(shù)表達式定義構(gòu)造函數(shù);
    Box.prototype.go = function(){       // 公有(特權(quán))方法;在原型上定義的;
      return age+run();
    };
  })();
  var box = new Box();
  console.log(box.go());             // 100運行中...;
// 上面的對象聲明,采用的是Box = function(){}而不是functiong Box(){};并且在聲明Box時沒有使用var關鍵字
// 導致:初始化未經(jīng)聲明的變量,總是會創(chuàng)建一個全局變量;因此,Box就成了一個全局變量,能夠在私有作用域之外被訪問到;
// 因為如果用函數(shù)聲明定義構(gòu)造函數(shù),那么就變成私有函數(shù)了,無法在全局訪問到了,所以要使用函數(shù)式定義構(gòu)造方法;
  (function(){
    var user = "";
    Person = function(value){          // 此處定義的Person是全局變量;
      user = value;              // 這里的構(gòu)造函數(shù)有權(quán)訪問私有變量name;
    };
    Person.prototype.getUser = function(){
      return user;
    };
    Person.prototype.setUser = function(value){
      user = value;
    }
  })();
  var person = new Person();
  person.setUser('Lee');
  console.log(person.getUser());          // =>Lee;
// 使用了prototype導致方法共享了,而user也就變成靜態(tài)屬性了;
// 所謂靜態(tài)屬性:即共享于不同對象中的屬性;?

八 模塊模式

// 簡言之,如果必須創(chuàng)建一個對象并以某些數(shù)據(jù)對其進行初始化,同時還要公開一些能夠訪問這些私有數(shù)據(jù)的方法,那么就可以使用模塊模式;
// 之前采用的都是構(gòu)造函數(shù)的方式來創(chuàng)建私有變量和特權(quán)方法;
// 那么對象字面量方式就采用模塊模式來創(chuàng)建;
  var box = {                   // 字面量對象,也是單例對象:只有一個實例的對象;
    age:100,                   // 這是公有屬性,將要改成私有;
    run:function(){
      return '運行中...';
    };
  };

// 模塊模式私有化變量和函數(shù):
  var box = function(){
    var age = 100;
    function run(){
      return '運行中...';
    }
    return {                   // 將一個字面量對象作為函數(shù)的值返回;
      go:function(){              // 返回的對象字面量中只包含可以公開的屬性和方法;
        return age+run();          // 由于這個對象是在匿名函數(shù)內(nèi)部定義的,因此它的公有方法有權(quán)訪問私有變量和函數(shù);
      }                  
    };                      // 從本質(zhì)上講,這個對象字面量定義的是單例的公共接口;
  }();
// 這種模式在需要對單例進行某些初始化,同時又需要維護其私有變量時是非常有用的; 

// 上面直接返回對象的例子,也可以這么寫:
  var box = function(){
    var age = 100;
    function run(){
      return '運行中...';
    }
    var obj = {                  // 創(chuàng)建字面量對象;
      go:function(){
        return age+run();
      }
    };
    return obj;                  // 返回剛創(chuàng)建的對象;
  }();

// 字面量的對象聲明,其實在設計模式中可以看作是一種單例模式;
// 所謂單例模式,就是永遠保持對象的只有一個實例;

// 增強的模塊模式:適合返回自定義對象,也就是構(gòu)造函數(shù);
  function Desk(){};
  var box = function(){
    var age = 100;
    function run(){
      return '運行中...';
    };
    var desk = new Desk();
    desk.go = function(){
      return age+run();
    };
    return desk;
  }();
  console.log(box.go());              // =>100運行中;

九 小結(jié)

在JavaScript編程中,函數(shù)表達式是一種非常有用的技術;使用函數(shù)表達式可以無須對函數(shù)命名,從而實現(xiàn)動態(tài)編程;

1.函數(shù)表達式

函數(shù)表達式不同于函數(shù)聲明;函數(shù)聲明要求有名字,但函數(shù)表達式不需要;
沒有名字的函數(shù)表達式叫做匿名函數(shù);2.閉包
當在函數(shù)內(nèi)部定義了其他函數(shù)時,就創(chuàng)建了閉包.閉包有權(quán)訪問包含函數(shù)內(nèi)部的所有變量;原理如下:
在后臺執(zhí)行環(huán)境中,閉包的作用域鏈包含著它自己的作用域、包含函數(shù)的作用域和全局作用域;
通常,函數(shù)的作用域及其所有變量都會在函數(shù)執(zhí)行結(jié)束后被銷毀;
但是,當函數(shù)返回了一個閉包時,這個函數(shù)的作用域?qū)恢痹趦?nèi)存中保存到閉包不存在為止;3.塊級作用域
使用閉包可以在JavaScript中模仿塊級作用域(JavaScript本身沒有塊級作用域的概念);要點如下:
創(chuàng)建并立即調(diào)用一個函數(shù),這樣既可以執(zhí)行其中的代碼,又不會在內(nèi)存中留下對該函數(shù)的引用;
結(jié)果就是函數(shù)內(nèi)部的所有變量都會被立即銷毀--除非將某些變量賦值給了包含作用域(即外部作用域)中的變量;4.私有變量
閉包還可以用于在對象中創(chuàng)建私有變量,要點如下:
即使JavaScript中沒有真是的私有對象屬性的概念,但是可以使用閉包來實現(xiàn)公有方法,而通過公有方法可以訪問包含作用域中定義的變量;
可以使用構(gòu)造函數(shù)模式、原型模式來實現(xiàn)自定義類型的特權(quán)方法,也可以使用模塊模式來實現(xiàn)單例的特權(quán)方法;

相關文章

  • iframe與主框架跨域相互訪問實現(xiàn)方法

    iframe與主框架跨域相互訪問實現(xiàn)方法

    今天正好需要用到iframe 與主框架相互訪問的實現(xiàn)方法,正好看到了這篇文章,確實不錯,特分享一下,需要的朋友可以參考下
    2017-09-09
  • JS等比例縮小圖片尺寸的實例

    JS等比例縮小圖片尺寸的實例

    JS等比例縮小圖片尺寸的實例,需要的朋友可以參考一下
    2013-02-02
  • JavaScript File分段上傳

    JavaScript File分段上傳

    這篇文章主要介紹了JavaScript File分段上傳的相關資料,需要的朋友可以參考下
    2016-03-03
  • JavaScript防抖與節(jié)流的實現(xiàn)與注意事項

    JavaScript防抖與節(jié)流的實現(xiàn)與注意事項

    防抖和節(jié)流嚴格算起來應該屬于性能優(yōu)化的知識,但實際上遇到的頻率相當高,處理不當或者放任不管就容易引起瀏覽器卡死,下面這篇文章主要給大家介紹了關于JavaScript防抖與節(jié)流的實現(xiàn)與注意事項,需要的朋友可以參考下
    2022-03-03
  • Websocket通信協(xié)議在數(shù)字孿生中的應用

    Websocket通信協(xié)議在數(shù)字孿生中的應用

    這篇文章主要為大家介紹了Websocket通信協(xié)議在數(shù)字孿生中的應用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • js操作iframe兼容各種主流瀏覽器示例代碼

    js操作iframe兼容各種主流瀏覽器示例代碼

    遇到了操作iframe的相關問題,其實就是在操作iframe內(nèi)部某個窗體時,調(diào)用父窗體的一個函數(shù),下面與大家分享下操作iframe兼容各種瀏覽器的方法
    2013-07-07
  • js數(shù)組Array sort方法使用深入分析

    js數(shù)組Array sort方法使用深入分析

    js中Array.sort()方法是用來對數(shù)組項進行排序的,默認是升序排列sort() 方法可以接受一個 方法為參數(shù),這個方法有兩個參數(shù),接下來本例將對sort方法進行深入探討,感興趣的朋友可以參考下
    2013-02-02
  • 微信小程序?qū)崿F(xiàn)IP歸屬地獲取功能

    微信小程序?qū)崿F(xiàn)IP歸屬地獲取功能

    在日常開發(fā)中,后端主要提供數(shù)據(jù)以及處理業(yè)務邏輯,前端主要提供頁面布局以及數(shù)據(jù)展示。本文主要介紹如何實現(xiàn)一個簡單獲取IP歸屬地的小程序,感興趣的可以了解一下
    2022-09-09
  • js中判斷文本框是否為空的兩種方法

    js中判斷文本框是否為空的兩種方法

    js中判斷文本框是否為空的兩種方法,需要的朋友可以參考下。
    2011-07-07
  • JavaScript中的this到底是什么(一)

    JavaScript中的this到底是什么(一)

    JavaScript中的this總是讓人迷惑,應該是js眾所周知的坑之一。 個人也覺得js中的this不是一個好的設計,由于this晚綁定的特性,它可以是全局對象,當前對象,或者…有人甚至因為坑大而不用this
    2015-12-12

最新評論