深入理解閉包系列第四篇——常見的一個迴圈和閉包的錯誤詳解

来源:http://www.cnblogs.com/xiaohuochai/archive/2016/08/03/5731641.html
-Advertisement-
Play Games

× 目錄 [1]犯錯 [2]IIFE [3]let 前面的話 關於常見的一個迴圈和閉包的錯誤,很多資料對此都有文字解釋,但還是難以理解。本文將以執行環境圖示的方式來對此進行更直觀的解釋,以及對此類需求進行推衍,得到更合適的解決辦法 犯錯 以上代碼的運行結果是2,而不是預想的0。接下來用執行環境圖示的 ...


×
目錄
[1]犯錯 [2]IIFE [3]let

前面的話

  關於常見的一個迴圈閉包的錯誤,很多資料對此都有文字解釋,但還是難以理解。本文將以執行環境圖示的方式來對此進行更直觀的解釋,以及對此類需求進行推衍,得到更合適的解決辦法

 

犯錯

function foo(){
    var arr = [];
    for(var i = 0; i < 2; i++){
        arr[i] = function(){
            return i;
        }
    }
    return arr;
}
var bar = foo();
console.log(bar[0]());//2    

  以上代碼的運行結果是2,而不是預想的0。接下來用執行環境圖示的方法,詳解到底是哪裡出了問題

  執行流首先創建併進入全局執行環境,進行聲明提升過程。執行流執行到第10行,創建併進入foo()函數執行環境,併進行聲明提升。然後執行第2行,將arr賦值為[]。然後執行第3行,給arr[0]和arr[1]都賦值為一個匿名函數。然後執行第8行,以arr的值為返回值退出函數。由於此時有閉包的存在,所以foo()執行環境並不會被銷毀

  執行流進入全局執行環境,繼續執行第10行,將函數的返回值arr賦值給bar

  執行流執行第11行,訪問bar的第0個元素並執行。此時,執行流創建併進入匿名函數執行環境,匿名函數中存在自由變數i,需要使用其作用域鏈匿名函數 -> foo()函數 -> 全局作用域進行查找,最終在foo()函數的作用域找到了i,然後在foo()函數的執行環境中找到了i的值2,於是給i賦值2

  執行流接著執行第5行,以i的值2作為返回值返回。同時銷毀匿名函數的執行環境。執行流進入全局執行環境,接著執行第11行,調用內部對象console,並找到其方法log,將bar[0]()的值2作為參數放入該方法中,最終在控制台顯示2

   由此我們看出,犯錯原因是在迴圈的過程中,並沒有把函數的返回值賦值給數組元素,而僅僅是把函數賦值給了數組元素。這就使得在調用匿名函數時,通過作用域找到的執行環境中儲存的變數的值已經不是迴圈時的瞬時索引值,而是迴圈執行完畢之後的索引值

 

IIFE

  由此,可以利用IIFE傳參和閉包來創建多個執行環境來保存迴圈時各個狀態的索引值。因為函數傳參是按值傳遞的,不同參數的函數被調用時,會創建不同的執行環境

function foo(){
    var arr = [];
    for(var i = 0; i < 2; i++){
        arr[i] = (function fn(j){
            return function test(){
                return j;
            }
        })(i);
    }
    return arr;
}
var bar = foo();
console.log(bar[0]());//0    

 

塊作用域

  使用IIFE還是較為複雜,使用塊作用域則更為方便

  由於塊作用域可以將索引值i重新綁定到了迴圈的每一個迭代中,確保使用上一個迴圈迭代結束時的值重新進行賦值,相當於為每一次索引值都創建一個執行環境

function foo(){
    var arr = [];
    for(let i = 0; i < 2; i++){
        arr[i] = function(){
            return i;
        }
    }
    return arr;
}
var bar = foo();
console.log(bar[0]());//0    

 

最後

  在編程中,如果實際和預期結果不符,就按照代碼順序一步一步地把執行環境圖示畫出來,會發現很多時候就是在想當然

  以上


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 2016-8-3 周三 做項目時遇到的問題: 每個div由迴圈變數輸出: {% for key,value in formextenddetail %} <div id="div_id_notes" class="value form-group row"> <div class="control- ...
  • 瀑布流佈局非常適合大量圖片的展示,一改過去裁剪圖片尺寸統一的排版,每張圖片都能完全展示,並錯落有致,讓人眼前一亮。 版本: jQuery v1.4.3+ jQuery Wookmark Load v1.4.8 註意事項: 項目中的 img 元素的 width 和 height 屬性需要寫,否則定位會 ...
  • 源代碼: <!DOCTYPE html><html><head> <title>中國移動官方網站</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="style.css"> <link rel="sh ...
  • 一、HTML和CSS 1、你做的頁面在哪些流覽器測試過?這些瀏覽器的內核分別是什麼? IE: trident內核 Firefox:gecko內核 Safari:webkit內核 Opera:以前是presto內核,Opera現已改用Google Chrome的Blink內核 Chrome:Blink ...
  • 利用sort()冒泡排序: 不聲明第三個變數冒泡排序: 第一層遍曆數組的個數(要遍歷多少次),第二次遍歷(共要迴圈幾次) a = 10; //第一個元素 b = 5; //下一個元素 if(a>b){ a = a+b; // a(15) = 10 +5; b = a-b; // b(10) = 15 ...
  • 方法一:使用border來設置邊框,元素有高度和寬度 效果: 利用transform屬性可以旋轉三角形,達到想要的效果。 方法二:利用border來撐起來三角形 效果: 應用場景:點擊234或者點擊選中的時候三角形指向對應的選項 小貼士: 1、學會經常使用偽元素例如after或者before來實現三 ...
  • 1.在項目中碰到了商品評價頁面,裡面有關於對商品的星星評價,當時的我只是把效果寫出來了(就是用戶點擊幾顆星星亮就顯現幾顆亮), 當我做好頁面交給後端同事的時候,他說我這樣做沒有意義他沒法做,那時我的腦子有些懵了。 後來後端同事說他來搗騰算了,作為一名21世紀的陽光好青年怎麼能夠把自己的任務都讓同事來 ...
  • 正則表達式蠻強大的哈,廢話不多說, 直接上整理內容 一:基本匹配符: \d 匹配數字 eg:'5\d0' >'580' \w 匹配字母或數字 eg:'\d\w\w' >'8zh' . 匹配除換行符外的任何一個字元任意字元 eg:'zh.' >'zh&' \s 空白符(tab)或空格 二:匹配字元長度 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...