1. 程式人生 > 實用技巧 >C# - Lambda表示式

C# - Lambda表示式

技術標籤:JavaScript前端javascriptjs

一、為什麼引入閉包

需求:給按鈕新增監聽,要求每次點選時提示點選的是第幾個按鈕

for (var i = 0,length=btns.length; i < length; i++) {
   var btn = btns[i]
   btn.onclick = function () {
     alert('第'+(i+1)+'個')
   }
 }

上面這段程式碼效果如下

在這裡插入圖片描述

給每個btn標籤的onclick事件執行時,本身onclick繫結的function的作用域中沒有變數i,i為undefined,則解析引擎會尋找父級作用域,發現父級作用域中有i,且for迴圈繫結事件結束後,i已經賦值為4,所以每個btn標籤的onclick事件執行時,alert的都是父作用域中的i,也就是4。這是作用域的問題。

此時可以通過閉包來解決這個問題(使用ES6語法:let宣告i也可以解決這個問題)

for (var i = 0,length=btns.length; i < length; i++) {
    (function (j) {
      var btn = btns[j]
      btn.onclick = function () {
        alert('第'+(j+1)+'個')
      }
    })(i)
  }

此時點選每個按鈕後,提示為點選的按鈕的序號。

二、閉包詳解

1.閉包的定義(MDN)

一個函式和對其周圍狀態(lexical environment,詞法環境)的引用捆綁在一起(或者說函式被引用包圍),這樣的組合就是閉包(closure)。也就是說,閉包讓你可以在一個內層函式中訪問到其外層函式的作用域。在 JavaScript 中,每當建立一個函式,閉包就會在函式建立的同時被創建出來。

上面這句話可以拆分理解為以下三句話:
1.閉包是巢狀的內部函式(絕大部分人認為)
2.如何產生閉包? 答:當一個巢狀的內部(子)函式引用了巢狀的外部(父)函式的變數(函式)時, 和執行最外層函式定義就會產生閉包(不用呼叫內部函式)。

  function fn1 () {
    var a = 2
    var b = 'abc'
    function fn2 () { //執行函式定義就會產生閉包(不用呼叫內部函式)
      console.log(a)
    }
  }
  fn1();

上面這段程式碼在fn1呼叫時就產生了閉包

2.閉包能幹什麼?

閉包常常用來「間接訪問一個變數」。換句話說,「隱藏一個變數」。

閉包可以在函式的外部訪問到函式內部的區域性變數。
還可以讓這些變數始終儲存在記憶體中,不會隨著函式的結束而自動銷燬。

 function fn1() {
   var a = 2
   function fn2() {
     a++
     console.log(a)
   }
   return fn2
 }
 var f = fn1()
 f() // 3
 f() // 4

上面這段程式碼在呼叫兩次後輸出結果為4是因為 f 對 fn2 的引用使變數a一直儲存在記憶體中。
所以在第二次呼叫 f 時a輸出為4。
此時閉包只產生了一次,因為外部函式只執行了一次(外部函式執行了幾次就有幾個閉包)

問題:
1. 函式執行完後, 函式內部宣告的區域性變數是否還存在? 一般是不存在, 存在於閉中的變數才可能存在
2. 在函式外部能直接訪問函式內部的區域性變數嗎? 不能, 但我們可以通過閉包讓外部操作它

3.閉包的缺點和優點

好處:
①保護函式內的變數安全 ,實現封裝,防止變數流入其他環境發生命名衝突
②在記憶體中維持一個變數,可以做快取(但使用多了同時也是一項缺點,消耗記憶體)
③匿名自執行函式可以減少記憶體消耗
壞處:
①其中一點上面已經有體現了,就是被引用的私有變數不能被銷燬,增大了記憶體消耗,造成記憶體、洩漏,解決方法是可以在使用完變數後手動為它賦值為null;
②其次由於閉包涉及跨域訪問,所以會導致效能損失,我們可以通過把跨作用域變數儲存在區域性變數中,然後直接訪問區域性變數,來減輕對執行速度的影響

4.閉包的應用

自定義JS模組
具有特定功能的js檔案
將所有的資料和功能都封裝在一個函式內部(私有的)
只向外暴露一個包信n個方法的物件或函式
模組的使用者, 只需要通過模組暴露的物件呼叫方法來實現對應的功能

外部JS檔案:

(function () {
  //私有資料
  var msg = 'Hello World'
  //操作資料的函式
  function doSomething() {
    console.log('doSomething() '+msg.toUpperCase())
  }
  function doOtherthing () {
    console.log('doOtherthing() '+msg.toLowerCase())
  }

  //向外暴露物件(給外部使用的方法)
  window.myModule = {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
})()

可以通過以下呼叫來呼叫外部檔案中的方法
myModule.doSomething()
myModule.doOtherthing()

參考:https://blog.csdn.net/weixin_43558749/article/details/90905723