JavaScript閉包(Closure)

来源:http://www.cnblogs.com/givebest/archive/2016/06/26/5617565.html
-Advertisement-
Play Games

本文收集了多本書里對JavaScript閉包(Closure)的解釋,或許會對理解閉包有一定幫助。 ...


JavaScript閉包(Closure)

本文收集了多本書里對JavaScript閉包(Closure)的解釋,或許會對理解閉包有一定幫助。

《你不知道的JavsScript》

  1. JavaScript 中閉包無處不在,你只需要能夠識別並擁抱它。
  2. 閉包是基於詞法作用域書寫代碼時所產生的自然結果。
  3. 當函數可以記住並訪問所在的詞法作用域時,就產生了閉包,即使函數是在當前詞法作用域之外執行。
  4. 無論通過何種手段將內部函數傳遞到所在的詞法作用域以外,它都會持有對原始定義作用域的引用,無論在何處執行這個函數都會使用閉包。
  5. 無論何時何地,如果將函數(訪問它們各自的詞法作用域)當作第一級的值類型併到處傳遞,你就會看到閉包在這些函數中的應用。在定時器、事件監聽器、 Ajax 請求、跨視窗通信、Web Workers 或者任何其它的非同步(或者同步)任務中,只要使用了回調函數,實際上就是在使用閉包。

《JavaScript編程解析》

1. 對閉包的初步認識

var fn = f();     // 將函數f 的返回值賦值給變數fn
fn();             // 1
fn();  //2
fn();   //3

function f() {
    var cnt = 0;
    return function() { return ++cnt; }
}

var fn1 = f1();
fn1(); //1
fn1(); //1

function f1(){
    var cnt = 0;
    return ++cnt;   
}

從錶面上來看,閉包是一種具有狀態的函數。或者也可以將閉包的特征理解為,其相關的局部變數在函數調用結束之後將會繼續存在。

2. 閉包的原理

  1. 閉包的前提條件是需要在函數聲明的內部聲明另一個函數(即嵌套的函數聲明)。
  2. 閉包指的是一種特殊的函數,這種函數會在被調用時保持當時的變數名查找的執行環境。
  3. 閉包僅僅是保持了變數名查找的狀態,而並沒有保持對象所有的狀態,對此請加以區分。也就是說,閉包雖然會保持(在嵌套外層進行函數調用時被隱式地生成的)Call 對象,但無法保持 Call 對象的屬性所引用的之前的對象的狀態。

3. 防範命名空間的污染

  1. 模塊
  2. 避免使用全局變數
  3. 通過實現信息隱藏

    //使用了閉包的模塊
    // 在此調用匿名函數
    // 由於匿名函數的返回值是一個函數,所以變數sum 是一個函數
    var sum = (function() {
     // 無法從函數外部訪問該名稱
     // 實際上,這變成了一個私有變數
     // 一般來說,在函數被調用之後該名稱就將無法再被訪問
     // 不過由於是在被返回的匿名函數中,所以仍可以繼續被使用
     var position = { x:2, y:3 };
    
     // 同樣是一個從函數外部無法被訪問的私有變數
     // 將其命名為sum 也可以。不過為了避免混淆,這裡採用其他名稱
     function sum_internal(a, b) {
         return Number(a) + Number(b);
     }
    
     // 只不過是為了使用上面的兩個名稱而隨意設計的返回值
     return function(a, b) {
         print('x = ', position.x);
         return sum_internal(a, b); };
     }
     )();
    
    // 調用
    sum(3, 4);
    x = 2
    7

在利用函數作用域可以封裝名稱,以及閉包可以使名稱在函數調用結束後依然存在這兩個特性後,信息隱藏得以實現。

(function() { 函數體 })();

4.閉包與類

  1. 計數器功能的類

    function counter_class(init) {                  // 初始值可以通過參數設定
        var cnt = init || 0;                        // 設置預設參數的習慣做法(參見5.5 節)
    
        // 如有必要,可在此聲明私有變數與私有函數
    
        return {
             // 公有方法
             show:function() { print(cnt); },
             up:function() { cnt++; return this; }, // return this 在使用方法鏈時很方便
             down:function() { cnt--; return this; }
        };
    }
    
    // 使用代碼
    var counter1 = counter_class();
    counter1.show();
    0
    counter1.up();
    counter1.show();
    1
    
    var counter2 = counter_class(10);
    counter2.up().up().up().show(); // 方法鏈
    13
  2. 表達式閉包

    JavaScript 有一種自帶的增強功能,稱為支持函數型程式設計的表達式閉包(Expression closure)。

    var sum = function(a, b) { return Number(a) + Number(b); }
    //可以省略為
    var sum = function(a, b) Number(a) + Number(b);

5.閉包與回調函數

《JavaScript高級程式設計》

  1. 閉包是指有權訪問另一個函數作用域中的變數的函數。創建閉包的常見方式,就是在一個函數內部創建另一個函數
  2. 當某個函數被調用時,會創建一個執行環境(execution context)及相應的作用域鏈。
  3. 由於閉包會攜帶包含它的函數的作用域,因此會比其他函數占用更多的記憶體。過度使用閉包可能會導致記憶體占用過多,我們建議讀者只在絕對必要時再考慮使用閉包。

記憶體泄漏

由於IE9之前的版本對JScript對象和COM對象使用不同的垃圾收集常式,因此閉包在IE的這些版本中會導致一些特殊的問題。具體來說,如果閉包的作用域鏈中保存著一個HTML元素,那麼就意味著該元素將無法被銷毀。

function assignHandler(){
    var element = document.getElementById("someElement");
    element.onclick = function(){
        alert(element.id);
    };
}

//把element變數設置為null。這樣就能夠解除對DOM對象的引用,順利地減少其引用數,確保正常回收其占用的記憶體。

function assignHandler(){
    var element = document.getElementById("someElement");
    var id = element.id;

    element.onclick = function(){
        alert(id);
    };

    element = null;
}

《JavaScript設計模式與開發實踐》

1. 閉包更多作用

  1. 封閉變數
  2. 延續局部變數的壽命

    //把img變數用閉包封閉起來,便能解決請求丟失的問題
     var report = (function(){
        var imgs = [];
        return function( src ){
            var img = new Image();
            imgs.push( img );
            img.src = src;
        }
    })();

2. 閉包與記憶體管理

  1. 局部變數本來應該在函數退出的時候被解除引用,但如果局部變數被封閉在閉包形成的環境中,那麼這個局部變數就能一直生存下去。如果在將來需要回收這些變數,我們可以手動把這些變數設為null。
  2. 跟閉包和記憶體泄露有關係的地方是,使用閉包的同時比較容易形成迴圈引用,如果閉包的作用域鏈中保存著一些DOM節點,這時候就有可能造成記憶體泄露。但這本身並非閉包的問題,也並非JavaScript的問題。
  3. 如果要解決迴圈引用帶來的記憶體泄露問題,我們只需要把迴圈引用中的變數設為null即可。將變數設置為null意味著切斷變數與它此前引用的值之間的連接。當垃圾收集器下次運行時,就會刪除這些值並回收它們占用的記憶體。

《JavaScript權威指南》

  1. JavaScript也採用詞法作用域(lexical scoping),也就是說,函數的執行依賴於變數作用域,這個作用域是在函數定義時決定的,而不是函數調用時決定的。為了實現這種詞法作用域,JavaScript函數對象的內部狀態不僅包含函數的代碼邏輯,還必須引用當前的作用域鏈。函數對象可以通過作用域鏈相互關聯起來,函數體內部的變數都可以保存在函數作用域內,這種特性在電腦科學文獻中稱為“閉包”。
  2. 從技術的角度講,所有的JavaScript函數都是閉包:它們都是對象,它們都關聯到作用域鏈。
  3. 是如果這個函數定義了嵌套的函數,並將它作為返回值返回或者存儲在某處的屬性里,這時就會有一個外部引用指向這個嵌套的函數。它就不會被當做垃圾回收,並且它所指向的變數綁定對象也不會被當做垃圾回收。

轉載請註明出處:http://www.cnblogs.com/givebest/p/5617565.html


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

-Advertisement-
Play Games
更多相關文章
  • Vue.js介紹 Vue.js是當下很火的一個JavaScript MVVM庫,它是以數據驅動和組件化的思想構建的。相比於Angular.js,Vue.js提供了更加簡潔、更易於理解的API,使得我們能夠快速地上手並使用Vue.js。 如果你之前已經習慣了用jQuery操作DOM,學習Vue.js時... ...
  • 使用js製作一個簡單的產品放大圖 購物網站的產品頁經常會放有一個產品展示圖區。該圖區有一個功能就是產品圖的放大功能,移動左側的焦點區域,可以放大細節部分觀看,詳情如下圖。實現該功能的方法也非常簡單。 實驗:製作產品焦點放大圖。 所需技能:1、基本的獲取頁面元素的方法; 2、幾個簡單的事件; 3、會使 ...
  • 簡介:jQuery Lightbox圖片放大預覽代碼是一款可以在用戶點擊頁面中的小圖片時,將該圖片的高清版本以Lightbox的方式放大顯示在頁面的中間,提高用戶的體驗度。效果展示 http://hovertree.com/texiao/jqimg/6/效果圖如下: 源碼下載:http://hove ...
  • 1.Ajax:readyState(狀態值)和status(狀態碼)的區別readyState,是指運行AJAX所經歷過的幾種狀態,無論訪問是否成功都將響應的步驟,可以理解成為AJAX運行步驟,使用“ajax.readyState”獲得status,是指無論AJAX訪問是否成功,由HTTP協議根據所 ...
  • 談到閉包,人們常常會把匿名函數和閉包混淆在一起。閉包是指由權訪問另一個函數作用域中的變數的函數。創建閉包的常見方式,就是在一個函數內部創建另一個函數,仍以前面的 createComparisonFunction()函數為例 在標識的部分,它訪問了外部的變數 propertyName 即使這個函數被返 ...
  • 第一部分:用CSS實現佈局 讓我們一起來做一個頁面 首先,我們需要一個佈局。 請使用CSS控制3個div,實現如下圖的佈局。 第二部分:用javascript優化佈局 由於我們的用戶群喜歡放大看頁面 於是我們給上一題的佈局做一次優化。 當滑鼠略過某個區塊的時候,該區塊會放大25%, 並且其他的區塊仍 ...
  • 概述 觀察者模式又叫發佈 訂閱模式(Publish/Subscribe),它定義了一種一對多的關係,讓多個觀察者對象同時監聽某一個目標對象(為了方便理解,以下將觀察者對象叫做訂閱者,將目標對象叫做發佈者)。發佈者的狀態發生變化時就會通知所有的訂閱者,使得它們能夠自動更新自己。 觀察者模式的使用場合就 ...
  • 關於NPM: npm 是 nodejs 的包管理和分發工具。它可以讓 javascript 開發者能夠更加輕鬆的共用代碼和共用代碼片段,並且通過 npm 管理你分享的代碼也很方便快捷和簡單。 一 NodeJs安裝與NPM管理 安裝 前往NodeJs官網,下載安裝包直接安裝。同時NodeJs預設包含N ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...