1. 程式人生 > >let解決for迴圈中的閉包

let解決for迴圈中的閉包

場景

  • 閉包產生

    • 內部函式依賴了外部作用域變數,即內部持有外部引用不釋放(延續了引用變數的生命週期,延壽)
    • 變數的本質其實就是一個佔位符,其值才是真正操作物件
    • 值可以是各語言的標量,也可以是記憶體地址(即通俗的引用型別)
  • var VS let

    • let 塊級用域(ES5之前的js不存在塊級作用域)
    • 在一個塊級作用域內,let 宣告 的變數只會在該區域記憶體續
    • var 不存在塊的問題,可以在塊外繼續生活
  • 與for的關係

    • var 初始變數在for迴圈體內,是覆蓋式的,用C的話來講是共用體,即共用同一記憶體地址
    • let初始變數在每一次for迴圈中,都是一個獨立的變數,擁有自己獨立的記憶體地址。
  • 函式體內部引用了內部不存在的變數,會尋找上層作用域內的同名變數

  • let + for + fn

    • for語句塊內的函式引用了該層let宣告變數
    • 函式引用了外部作用域 變數不會主動釋放,即若該函式被呼叫該變數會存活於記憶體中。
  • 小結

    • for迴圈體內定義fn ,若函式體內用了for塊var變數,在for語句外呼叫該函式,該函式採用的var值是迴圈結束後的var值
    • 而塊內用let變數,與之同級的函式體用了該let變數,之後呼叫函式,函式使用的是定義時塊內的let變數值。
    • 關鍵是否使用同一值(或址)

程式碼

js生成高德地圖示記
 buildMarkers() {
      // 初始化標記陣列
      this.markers = [];

      var image = ROAST_CONFIG.APP_URL + "/storage/img/coffee-marker.png";
      // 自定義點標記
      var icon = new AMap.Icon({
        image: image,
        imageSize: new AMap.Size(19, 33)
      });

      for (var i = 0; i < this.cafes.length; i++) {
        // 為每個咖啡店建立點標記並設定經緯度
        var marker = new AMap.Marker({
          position: new AMap.LngLat(
            parseFloat(this.cafes[i].longitude),
            parseFloat(this.cafes[i].latitude)
          ),
          title: this.cafes[i].location_name,
          icon: icon,
          // 標記額外資料,方便過濾
          extData: {
            cafe: this.cafes[i]
          },
          map: this.map,
          clickable: true
        });

        // 自定義窗體資訊,必須使用let生成塊級作用域,此變數在for語句塊中每一次迴圈中都是一個新變數,準確的來說是一個新的記憶體地址,產生了新副本執行時就固定了下來
        // 理由mark點選事件處理器依賴了該變數。而在js中閉包依賴,會尋找上級作用域(如果上級有作用域的話)內的值的引用,從而不會釋放
        // 用var的話,由於在for區間內不存在塊作用域,其infoWindow值是for迴圈結束後的值
        let infoWindow = new AMap.InfoWindow({
          content: this.cafes[i].name +'_'+ this.cafes[i].location_name
        });

        // 繫結點選事件到點標記物件,點選開啟上面建立的資訊窗體
        marker.on("click", function() {
          infoWindow.open(this.getMap(), this.getPosition());
        });

        this.infoWindows.push(infoWindow);

        // 將標記放到陣列中
        this.markers.push(marker);
      }
      // 將所有的標記顯示到地圖
      this.map.add(this.markers);
    },