作用域、執行環境、閉包(四)

来源:http://www.cnblogs.com/mysky7511/archive/2017/04/17/6723788.html
-Advertisement-
Play Games

本文也同步發表在我的公眾號“我的天空” 上一期我們已經介紹了閉包,由於閉包可以延長函數內部的變數的生存周期,因此我們可以將不需要暴露在全局的變數封裝成函數的內部變數,從而避免代碼污染。 譬如要實現一個簡單的累加器,為了保存每次累加的結果,因此聲明瞭一個全局變數total,代碼如下: var tota ...


本文也同步發表在我的公眾號“我的天空

 

 

上一期我們已經介紹了閉包,由於閉包可以延長函數內部的變數的生存周期,因此我們可以將不需要暴露在全局的變數封裝成函數的內部變數,從而避免代碼污染。

 

譬如要實現一個簡單的累加器,為了保存每次累加的結果,因此聲明瞭一個全局變數total,代碼如下:

 

var total=0;
function add(t){
    total+=t;
    alert(total);
}
total=2;
add(3);        //顯示5
add(5);        //顯示10
add(1);        //顯示11

 

但是在實際開發中,應儘量避免全局變數,因為全局變數可以在代碼的任何地方被調用,假設在其他地方,不小心更改了total的值的話,我們這個累加器就會出問題了。由於total值是只供函數add()使用的,因此希望total能被封閉在函數add()的內部,這樣,就無法從外部來改寫它了,我們使用閉包來實現,代碼如下:

 

function add(s){
    var total=s;
    return function(t){
        total+=t;
        alert(total);
    }
}
var a=add(2);
a(3);        //顯示5
a(5);        //顯示10
a(1);        //顯示11

 

通過閉包,將變數total封閉在函數add()中,外部無法訪問到,這樣就避免了被其他代碼隨意改寫的可能性。

 

由於閉包會將封閉在函數內部的局部變數賦予類似於全局變數的效果,因此在有些場景下需要特別註意,尤其是涉及到迴圈遍歷,來看以下代碼:

 

function createArray(){
    var result=new Array();
    for (var i=0;i<3;i++){
        result[i]=function(){
            return i;
        }
    }
}

 

這是一個創建數組數組的函數,從錶面上看,每個函數應該都返回自己的索引值,因此創建的數組中,每個元素應該包含如下函數:

 

result[0]:function(){return 0}
result[1]:function(){return 1}
result[2]:function(){return 2}

 

但是實際上,數組中的每個元素只是包含:function(){return i},也就是說,當該函數執行完畢後,返回的是這樣一個數組:

 

{
    function(){return i},
    function(){return i},
    function(){return i}
}

 

而變數i由於存在於一個返回函數中,形成了閉包,所以當createArray()執行完畢後,其執行環境不會被銷毀,變數i得以保留,並且其值為3(這點很重要)。

 

因此,當我們使用createArray()來創建數組時,得到的效果就不是我們的預期,彈出的都為“3”:

 

var a=createArray();
for(var z=0;z<a.length;z++){
    alert(a[z]());    //均顯示為3
}

 

實際上該代碼無非就是重覆執行三遍以下代碼:

 

alert(function(){return 3}());


那麼為了達到我們的預期,應該將createArray()函數做如下修改:

 

function createArray(){
    var result=new Array();
    for (var i=0;i<3;i++){
        result[i]=function(z){
            return function(){
                return z;
            };
        }(i)
    }
}

 

分析以上代碼,我們將一個自執行函數返回給了數組元素,在賦值的時候,變數z就是在賦值的那個時刻的i值,那麼返回的數組中的元素便包含我們預期的函數:

 

result[0]:function(){return 0}
result[1]:function(){return 1}
result[2]:function(){return 2}

 

再一次執行以下代碼,顯示就正常了:

 

var a=createArray();
for(var z=0;z<a.length;z++){
    alert(a[z]());    //依次顯示0、1、2
}


一定要註意的是,我們是把一個函數賦予了數組中的元素,而不是單個的值。因為在實際的應用中,返回函數的話,我們就可以在函數內做更多的事情。

 

看以下的實現,html有四個p標簽和4個div標簽,當單擊div標簽時相應的p標簽更改顏色,請註意這是一個面試中非常容易遇到的題目,代碼如下:

 

<body>
  <p>p1</p><p>p2</p><p>p3</p><p>p4</p>
  <div>div1</div><div>div2</div><div>div3</div><div>div4</div>  
 </body>

  <script>
     var d=document.getElementsByTagName("div");
       for(var i=0;i<d.length;i++){
         d[i].onclick=function(num){
             return function(){
                document.getElementsByTagName("p")[num].style.color="red";
             };
         }(i);
       }
 </script>

 

如果直接寫成以下代碼的話,那麼無論你單擊哪個div,程式總是會報錯,因為此時i的值為4,所以document.getElementsByTagName("p")[4]這個元素並不存在,導致引用錯誤。

 

   for(var i=0;i<d.length;i++){
        d[i].onclick=function(){
          document.getElementsByTagName("p")[i].style.color="red";
      };
   }

 

最後我們要註意的是當需要返回函數內部的多個變數時,便不能採用返回匿名函數的方式了,可以採用以下的形式:

 

function setpepole(){
    var name="李四";    
    var age=31;
    return {
        getname:funcion(){
            return name;    
        },
        getage:function(){
            return age;
        }
    }
}
var a=setpepole();
alert(a.getname());     //顯示“李四”
alert(a.getage());      //顯示31

 

閉包系列就到此全部結束了!

 


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

-Advertisement-
Play Games
更多相關文章
  • 這幾天在學jQuery,本身還只是一個新手,寫了一個簡單的動畫——圓形頭像的縮放。本身是用Firefox進行調試的,一切進行的很順利,縮放可以按照預期執行,結果拿到IE上去之後,發現縮放動畫失效了。後來百度了一些東西都沒有找到關鍵所在,最後Google一下,找到了很多實用的解決方法,現在在這裡總結一 ...
  • Bulma 是一個基於 Flexbox 的現代化的 CSS 框架,設計的初衷就是移動優先(Mobile First),模塊化設計,可以輕鬆用來實現各種簡單或者複製的內容佈局,瀏覽器支持:Chrome、Edge、Firefox、Internet Explorer (10+)、Opera 以及 Safa... ...
  • 任務一:零基礎HTML編碼 課程概述 作業提交截止時間:04-24 重要說明 百度前端技術學院的課程任務是由百度前端工程師專為對前端不同掌握程度的同學設計。我們儘力保證課程內容的質量以及學習難度的合理性,但即使如此,真正決定課程效果的,還是你的每一次思考和實踐。 課程多數題目的解決方案都不是唯一的, ...
  • 用 js 的 selection range 方法操作選擇區域內容和圖片,實現選擇、刪除等操作。 ...
  • 1.前臺javascript 1.在提交的js中這樣寫 document.form1.username.value=encode64(document.form1.username.value); document.form1.password.value=encode64(document.for... ...
  • 引子:用javascript給元素綁定事件,我們可以用addEventListener這個方法,然而這個方法有相容問題,比如在IE瀏覽器上面就無效,在IE上面要用attachEvent這個方法 一、addEventListener和attachEvent的區別: 1、addEventListener ...
  • git:https://github.com/reg21st/vue2 management platform 訪問:https://reg21st.github.io/vue2 management platform 概述: 最近學習vue2.0和elementUI的使用,在各種文檔的幫助下,嘗試 ...
  • 語句 (建議)每條語句放在不同的行上並加上分號 first cnblogs;second cnblogs; 註釋 (建議)用"//" 來註釋單行,用"/*"註釋多行 //有註釋是好事 /*有註釋是好事 有註釋是好事 有註釋是好事 有註釋是好事*/ 變數 JS語法不允許變數名中包含空格或標點符號(美元 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...