JavaScript--我發現,原來你是這樣的JS:函數表達式和閉包

来源:http://www.cnblogs.com/Ry-yuan/archive/2017/12/09/7868827.html
-Advertisement-
Play Games

一、介紹 本次博客主要介紹函數表達式的內容,主要是閉包。 二、函數表達式 定義函數的兩種方式:一個是函數聲明,另一個就是函數表達式。 區別: 1.函數聲明是用function後面有函數名,函數表達式是賦值形式給一個變數。 2.函數聲明可以提升函數,而函數表達式不會提升 函數提升就是函數會被自動提升到 ...


一、介紹

本次博客主要介紹函數表達式的內容,主要是閉包。

二、函數表達式

定義函數的兩種方式:一個是函數聲明,另一個就是函數表達式。


//1.函數聲明寫法
function fn2(){
    console.log('函數聲明');  
}
//2.函數表達式寫法
var fn1 = function(){
    console.log('函數表達式');
}

區別:
1.函數聲明是用function後面有函數名,函數表達式是賦值形式給一個變數。
2.函數聲明可以提升函數,而函數表達式不會提升

函數提升就是函數會被自動提升到最前方,以至於再調用函數後再聲明函數也不會有錯:

//例子:
//先調用運行
sayName();
//再聲明函數
function sayName(){
    console.log('ry');
}

//運行結果
'ry'

函數表達式就不會被提升:

//先調用
sayBye();
//函數表達式
var sayBye = function(){
    console.log('bye bye');
}

//運行報錯

但是下麵的寫法很危險:因為存在函數聲明的提升

//書上代碼
if(condition){
    function sayHi(){
        console.log('hi');
    }
}
else{
    function sayHi(){
        console.log('yo');
    }
}

解說一下: 這段代碼想表達在condition為true時聲明sayHi,不然就另一函數sayHi,但是運行結果往往出乎意料,在當前chrome或firefox可能能做到,但是在IE10以下的瀏覽器(我測試過)往往不會遵循你的意願,不管condition是true還是false都會輸出yo。

這時函數表達式能派上用場了:

//換成函數表達式,沒問題因為不會被提升,只有當執行時才賦值
var sayHi = null;
if(condition){
    sayHi = function (){
        console.log('hi');
    }
}
else{
    sayHi = function sayHi(){
        console.log('yo');
    }
}

三、閉包

閉包的定義:有權訪問另一個函數作用域中的變數的函數

有人覺得閉包很難理解,一開始我也是這樣的,我認為那是對一些概念還不夠瞭解。

定義中說明瞭什麼是閉包,最常見的形式就是在函數中再聲明一個函數。

重點理解這裡:

1.閉包能訪問外圍函數的變數是因為其作用域鏈中有外圍函數的活動對象(這個活動對象即使在外圍函數執行完還會存在,不會被銷毀,因為被閉包引用著)。

2.閉包是函數中的函數,也就是說其被調用時也創建執行上下文,對於執行上下文這部分可以看看這篇:深入理解js執行--創建執行上下文這篇博客。

理解了上面之後我們再來看閉包的例子:

function a(){
    //a函數的變數
    var val_a = "我是a函數里的變數";
    //聲明函數b,b能訪問函數a的變數
    function b(){
        console.log(val_a);
    }
    //a函數將b返回
    return b;
}

//全局變數fn,a執行返回了b給fn
var fn = a();
//調用fn,能夠在全局作用域訪問a函數里的變數
fn();  //我是a函數里的變數

這裡fn能夠訪問到a的變數,因為b中引用著a的活動對象,所以即使a函數執行完了,a的活動對象還是不會被銷毀的。這也說明過度使用閉包會導致記憶體泄漏。

再來個常見的例子(給多個li添加點擊事件,返回對於li的下標):

<body>
    <ul id="list">
        <li>red</li>
        <li>green</li>
        <li>yellow</li>
        <li>black</li>
        <li>blue</li>
    </ul>
</body>
//獲得li標簽組
var li_group = document.getElementsByTagName('li');

//錯誤例子:每個li都會跳出5
function fn(){
    //為每一個li添加點擊事件
    var i = 0;
    //使用for來給每個li添加事件
    for(;i<li_group.length;i++){
        //添加點擊事件
        li_group[i].addEventListener('click',function(){
            // 輸出對應得下標
            console.log(i);
        });
    }
}
fn();


//正確的例子:
//在加一層的函數,作為閉包,用來保存每一次迴圈的i變數,就可以達到目的
function correctFn(){
    var i = 0;
    for(;i<li_group.length;i++){
        //在外面套一層函數,這層函數會保存每次迴圈的i變數,也就是閉包了。
        (function(num){
            li_group[num].addEventListener('click',function(){
                console.log(num);
            });               
        }(i));        
    }
}
correctFn();

看下麵解釋之前我預設你已經知道活動對象是什麼了,如果不懂可以看這篇:深入理解js執行--創建執行上下文

解釋一下:
1.錯誤的例子:
屢一下思路,每個li都有click執行的函數,每個函數點擊後才會執行是吧,那每個click的函數的外層函數是fn這個函數,那這5個click函數都會保存著fn的活動對象,那這個輸出的i變數在fn這函數裡面,所以當fn執行完後,i的值是5了,因此當每個裡觸發click的函數的時候輸出的也就是5了。
再簡單的說:每個li的click事件觸發的函數引用的i在同一個活動對象中,所以值都一樣。

2.正確執行的例子:
我們就在外層加了一層匿名函數並立即執行(雖然函數被執行了,如果有函數引用著它的活動對象,那其活動對象將不滅),這時每個li的click函數外層函數是每次迴圈產生的不同的匿名函數,這匿名也是有活動對象,每個li的click的函數保存著各自的匿名函數的活動對象,num這變數也根據每次迴圈產生不同的匿名函數傳入的i的不同而不同,所以能夠輸出對應不同的值。

上面說的可能有點啰嗦,請原諒我[捂臉.jpg],我是希望儘可能的表達清楚。如果你看懂了,那對閉包的理解也更深一層了哦。

小結:

1.本篇主要講的是閉包,閉包是有權訪問另一個函數作用域中的變數的函數,主要是函數中的函數,因為能引用外層函數的活動對象所以能夠訪問其外層的變數。
2.我本篇主要講的是原理,如果對一些東西不懂,可以看下麵幾篇。

相關的幾篇:
深入理解js執行--單線程的JS
深入學習JS執行--創建執行上下文(變數對象,作用域鏈,this)
我發現,原來你是這樣的JS全部文章彙總(點擊此處)

本文出自博客園:http://www.cnblogs.com/Ry-yuan/
作者:Ry(淵源遠願)
歡迎訪問我的個人首頁:我的首頁
歡迎訪問我的github:https://github.com/Ry-yuan/demoFiles
歡迎轉載,轉載請標明出處,保留該欄位。


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

-Advertisement-
Play Games
更多相關文章
  • 學過Angular的同學都知道,輸入框通過 實現雙向數據綁定,那麼自定義組件能不能實現雙向數據綁定呢?答案是肯定的。 Angular中,我們常常需要通過方括弧 和圓括弧 實現組件間的交互: 那麼在 和`()`的基礎上,如何實現組件的雙向數據綁定? 例子如下。 子組件: 註意這裡的寫法,這是關鍵所在, ...
  • DOM(文檔對象模型)是針對HTML和XML文檔的一個API,描繪了一個層次化的節點樹,允許開發人員添加、刪除和修改頁面的某一部分。 HTML DOM 樹形結構如下: 1.Node方面 1.1 節點類型 確定節點類型,相容的方法是將nodeType屬性與數字值進行比較,如下所示: if(someNo ...
  • 前言 網站的佈局是一個網站設計的根本,CSS的Grid佈局已經成為了未來網站佈局的基本方式。 今天這篇文章我們通過圖文,一起看看如何自己實現Grid佈局方式。 CSS 第一個Grid佈局 首先我們看看最基本的Grid佈局是什麼樣的,HTML頁面的代碼如下所示。 HTML代碼 然後設置其CSS屬性,這 ...
  • 背景 : 一日晚上下班的我靜靜的靠在角落上聽著歌,這時"滴!滴!"手機上傳來一陣qq消息。原來我人在問王者榮耀的雷達圖在頁面上如何做出來的,有人回答用canvas繪畫。那麼問題來了,已經好久沒有使用canvas繪畫了東西。 SO ,就想自己畫一個canvas雷達圖,順便重新回顧一下canvas的知識 ...
  • 今天用到城市選擇,直接用拼音滑動方式來選擇,用的時候引入jquery(個別樣式需要自己修改) 最終效果圖 ...
  • web服務 處於應用層的http協議負責的數據傳輸與解析。位於socket上層,用socket傳輸http數據時需要在消息開頭處聲明是http協議/相應http版本 狀態碼 狀態碼含義 \r\n\r\n 真正的字元串內容。 HTML是什麼 Hypertext Markup Language,是一種標 ...
  • 前言 網路早期最大的問題之一是如何管理狀態。簡而言之,伺服器無法知道兩個請求是否來自同一個瀏覽器。當時最簡單的方法是在請求時,在頁面中插入一些參數,併在下一個請求中傳回參數。這需要使用包含參數的隱藏的表單,或者作為URL參數的一部分傳遞。這兩個解決方案都手動操作,容易出錯。 網景公司當時一名員工Lo ...
  • 註:本文轉載自大神同學的博客,http://www.cnblogs.com/st-leslie/p/5617130.html ,僅供學習不用於其他用途,大家想要更多的乾貨,請移步到該大神博客園 一、什麼是localStorage、sessionStorage 在HTML5中,新加入了一個localS ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...