淺談js閉包

来源:http://www.cnblogs.com/wubetter/archive/2016/07/14/5668525.html
-Advertisement-
Play Games

相信很多人只知道閉包這個詞但是具體是怎麼回事就不太清楚了,最近在群里有很多小伙伴討論這個問題但還是矇矓矓的趕腳。索性就寫了這篇文章來幫助大家一起理解閉包。 變數作用域 閉包其實想明白了很簡單,但是在理解閉包之前,我們先溫習一下作用域的概念不多說 直接上代碼來的直接 全局變數 局部變數 我們大家都知道 ...


相信很多人只知道閉包這個詞但是具體是怎麼回事就不太清楚了,最近在群里有很多小伙伴討論這個問題但還是矇矓矓的趕腳。索性就寫了這篇文章來幫助大家一起理解閉包。

變數作用域

閉包其實想明白了很簡單,但是在理解閉包之前,我們先溫習一下作用域的概念不多說 直接上代碼來的直接

  • 全局變數

    var a = 1;
    function f1(){
    a++;
    console.log(a);
    }
    f1();//2
    f1();//3
  • 局部變數

    function f1(){
    var a = 1;
    a++;
    console.log(a);
    }
    f1();//2
    f1();//2

    我們大家都知道在函數內部定義的變數(使用var)為局部變數,在函數內部可以訪問函數外部的變數和函數,但是在函數外部就訪問不了函數內部的變數和函數了

閉包就是能夠讓我們在函數外部能訪問到函數內部的變數和函數下麵我們看一個史上最簡單的一個閉包,真的不能再簡單,你相信我

function f1(){
    var str ='我在函數裡面,外面的壞人找不到我!!哈哈';
    var aa = function(){
        alert(str);
    }
    return aa;
}
var b = f1();
b();

在這個例子中f1中的函數aa 就是一個閉包,什麼 aa 不是一個函數麽?那閉包也是一個函數咯? 對,閉包說白了 就是一個函數,只不過這個函數比較特殊而已,特殊到什麼地方,從例子不難發現 aa 是聲明在函數f1 內部的,同時又把這個函數aa當成返回值給return 出來了。然後 我們執行 var b = f1() 就相當於 var b = aa 但是 aa 是在函數內部定義的 通常情況下外部是無法訪問的,這個時候我們就把這個函數aa 當成函數的返回值給拋出去,這樣在外層就能訪問到 aa了 由於 aa 又是在函數f1內部聲明的 所以 我們變相的可以訪問到函數f1中的變數。這就是閉包最基本的作用

同時它還有很多好處:

  • 希望一個變數長期駐扎在記憶體中
  • 避免全局變數的污染
  • 私有成員的存在

閉包的特性:
1.函數嵌套函數
2.函數內部可以引用外部的參數和變數
3.參數和變數不會被垃圾回收機制回收

簡單的相信大家都能看懂,但是遇見覆雜的就矇蔽的,特別是和對象在一塊
就拿今天群里討論的一個例子來說吧

var sister = '大桃花';
var obj = {
    sister:'大妹噠',
    sis:function(){
        var sister = '小妹噠';
        return function(){
            console.log(sister);
            console.log(this.sister);
        };
    }
};
function place(){
    var sister = '大福晉';
    var girl = obj.sis();
    girl();
}
place();

在這個例子中 糊弄人的就是 sister 在不同的作用域中都有定義,大兄弟莫慌張!我們一起來揭開它這神秘的面紗,看看到底是大妹噠還是小妹噠,place 調用 place內部聲明瞭一個 sister 然後緊接著 又定義一個 girl = obj.sis();obj的sis方法 的返回值是一個函數 也就是說 girl最後被賦值為一個函數,先不要管這個函數是在哪,反正就是一個函數,然後緊接著girl被調用,再被調用之前 你要明白下麵的這些東西函數聲明的時候會創建一個作用域鏈 這個鏈條上面都有神馬東西

  • this
  • 函數內部的變數 --->此函數聲明時所在的作用域中的變數 --->....--->window(瀏覽器中的最頂層)
  • 函數參數(arguments

這3項中其中後兩項是在函數聲明的時候就被確定下來了,也就是說 函數的作用域鏈條是在函數聲明的時候就確定了,而非是函數調用的時候,然而this 在函數聲明是時不確定,而是在函數被調用的時候所確定的,至於怎麼確定那就要看函數被調用時所在的對象,函數被調用無非就2中形式:

  • 直接調用(比如 getName()
  • 通過對象.函數的形式調用(比如 obj.getName()

這兩種方式調用 第一種的調用方式中 函數中的this 是永遠指向 window的(這裡拋開 callapply);而第二種調用方式 函數中(這裡通常稱作方法)的this 是指向點前面的obj

下麵我們再看上面的例子
先說 console.log(sister)
由於girl是一個函數的引用 ,而這個函數是在 obj.sis 函數中聲明的 只不過是一個匿名函數而已,由於這個匿名函數內部和匿名函數的參數中都沒有 sister 這個變數, 所以順著作用域鏈往上查找 找到 obj.sis函數的作用中聲明瞭 sister這個變數 找到之後就不再去往上查找(js中的很多機制都是這中情況 比如 原型集成中對象屬性的查找規則也類似這樣)這個時候 控制台就會輸出 obj.sis中定義的 sister變數的值。

再來看 console.log(this.sister)
girl這個函數被調用時的環境 是 place函數內部 而 place 是在整個window作用域下的 所以 這裡的this 是指向 window 的 因而 控制台輸出結果 會是 window下的 sister

再看下麵的例子

var a = 'window';
var sys = {
    a:'sys',
    getA:function(){
        alert(this.a);
    },
    getAA:function(){
        return function(){
            alert(this.a);
        }
    }
}
sys.getA();//sys
sys.getAA()();//window

這個例子中sys.getA() 不用說 大家都能理解,第二個 可以這麼來看 先看sys.getAA() 這個結果其實是一個函數你可以把他當成一個函數的名字 a ,變換一下寫法 就是 sys.getAA()() 等價於:

var a = sys.getAA();
a();

由於 這是函數的直接調用 所以 這裡的this指向 window 最後結果 就是 'window'.

就先說這麼多吧 思路可能有點混亂,如果你有什麼好的建議或者意見,歡迎指正!


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

-Advertisement-
Play Games
更多相關文章
  • [1]條件 [2]逗號 [3]賦值 [4]() [5]void ...
  • cubic-bezier即為貝茲曲線中的繪製方法。圖上有四點,P0-3,其中P0、P3是預設的點,對應了[0,0], [1,1]。而剩下的P1、P2兩點則是我們通過cubic-bezier()自定義的。cubic-bezier(x1, y1, x2, y2) 為自定義,x1,x2,y1,y2的值範圍 ...
  • 希望這篇博客可以對你有所幫助,如果有什麼技術上的問題,希望我們可以做進一步的交流,如果你覺得我哪裡闡述的不正確或者你有更好的更透徹的理解,也可以聯繫我,我在這裡隨時等著你。 對於css/html是每個前端必經之路,之前我們可以只是簡單用css去做一些靜態的界面佈局,需要動畫效果還是需要js的幫助才可 ...
  • ClockPicker.js是一款時鐘插件,其實還可以改進,裡面的分可以改成短橫線。 線上實例 實例預覽 jQuery ClockPicker 圓形時鐘 使用方法 複製 參數詳解 ClockPicker調用方法 下載 ...
  • CSS3為我們帶來了令人驚嘆的新特性,而最有趣的就是CSS動畫。向大家推薦這50個CSS動畫集合可以讓你通過使用JavaScript函數來讓動畫更生動。為了能夠預覽到這些驚人的CSS3技術帶來的動畫特效,請大家使用如Safari和Chrome這類基於WebKit內核的瀏覽器。(IE瀏覽器謝絕觀賞~) ...
  • 方法一: // 計算系統當前是星期幾 var str = "今天是星期" + "日一二三四五六".charat(new date().getday()); 方法二: var a = new array("日", "一", "二", "三", "四", "五", "六"); var week = ne ...
  • 針對之前遇到過的一些特殊樣式的實現,我今天做個總結,目的有二:一是將這些方法記錄下來,以便將來需要用到時查找使用。二為將這些大神們智慧的結晶發揚光大,讓廣大前端程式猿們能夠少走彎路。此貼為更新帖,以後若有好的css樣式技巧,小哥我會不定期更新。 一、塊元素水平垂直居中(特別鳴謝:鑫生活。鑫哥出品必屬 ...
  • node.js越來越熱,應用的場景也越來越多。 但也因為是開源軟體,所以具備大多數開源軟體都存在的“版本問題”,版本發展很快,版本前後差異性大,老系統用新版本node跑不過,全局安裝的第三方組件和node版本相關造成全局版本混亂。 nvm是解決這一問題的利器。 nvm是node版本管理工具,主要特點 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...