前情紀要:上次的公司例會上的分享會上我準備了一個關於「閉包」的ppt,而後本身向部門的同事們講解了一下我對於這部份內容的理解,可是講完了以後發現本身還有不少地方說的不是很透徹。javascript
一:背景知識小解:java
這裏要講的閉包實際上是「javascript 中的閉包」要理解閉包的概念首先要知道一些關於js(javascript的簡稱,下面就js)的基礎知識編程
Javascript特殊的變量做用域。 swift
變量的做用域無非就是兩種:全局變量和局部變量。瀏覽器
Javascript語言的特殊之處,就在於函數內部能夠直接讀取全局變量。 緩存
另外一方面,在函數外部天然沒法讀取函數內的局部變量。閉包
介紹到這裏可能有人要問,爲啥不能讀取框架
這就是Javascript語言特有的「鏈式做用域」結構(chain scope),函數
子對象會一級一級地向上尋找全部父對象的變量。因此,父對象的全部變量,性能
對子對象都是可見的,反之則不成立。
垃圾回收機制(garbage collection):垃圾收集器會按期(週期性)找出
那些不在繼續使用的變量,而後釋放其內存。
再也不使用的變量也就是生命週期結束的變量,固然只多是局部變量,
全局變量的生命週期直至瀏覽器卸載頁面纔會結束。
二:初始閉包:
這就是最簡答的一個「閉包」,
函數」和「函數內部能訪問到的變量」(也叫環境)的總和,就是一個閉包。
編程界崇尚以簡潔優雅惟美,不少時候 若是你以爲一個概念很複雜,那麼極可能是你理解錯了。
還要稍微複雜一點的閉包:
三:如何理解閉包:
閉包經常用來「間接訪問一個變量」。換句話說,「隱藏一個變量」。 假設咱們在作一個遊戲,在寫其中關於「還剩幾條命」的代碼。 若是不用閉包,你能夠直接用一個全局變量: window.lives = 30 // 還有三十條命 這樣看起來很不妥。萬一不當心把這個值改爲 -1 了怎麼辦。因此咱們不能讓別人 「直接訪問」這個變量。怎麼辦呢? 用局部變量。 可是用局部變量別人又訪問不到,怎麼辦呢? 暴露一個訪問器(函數),讓別人能夠「間接訪問」。那麼在其餘的 JS 文件,就可使用 window.獎勵一條命() 來漲命,使用 window.死一條 命() 來讓角色掉一條命。
其實我以爲閉包就是這樣一個很簡單的概念,這種概念不光光是應用在js中,
在PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等語言中都能找到對閉包不一樣程度的支持。
因此說對於技術來講,基本的原理都是相同的,就像如今的js中有不少在使用面向對象的思路在寫框架、寫接口、寫插件。
四:閉包的幾種表現形式
一、匿名自執行函數
var data= { table : [], tree : {} }; (function(dm){ for(var i = 0; i < dm.table.rows; i++){ var row = dm.table.rows[i]; for(var j = 0; j < row.cells; i++){ drawCell(i, j); } } })(data);
( function() {}() ); ( function() {} )(); [ function() {}() ]; ~ function() {}(); ! function() {}(); + function() {}(); - function() {}(); var f = function() {}(); 下面再講-
2:結果緩存
3:封裝
4:繼承
簡單講一下3:封裝
var person = function(){ //變量做用域爲函數內部,外部沒法訪問 var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }(); alert(person.name);//直接訪問,結果爲undefined alert(person.getName()); person.setName("abruzzi"); alert(person.getName()); (演示一下吧!) 獲得結果以下: undefined default abruzzi
小總結:學習過有關「面向對象「原理的高級語言的同窗應該看出來了,這就是面向對象的思想,因此再次強調,技術是相同的
五:閉包的優缺點
優勢:
1:能夠讀取函數內部的變量,
2:讓這些變量的值始終保持在內存中,
缺點:
(1)因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露。解決方法是,在退出函數以前,將不使用的局部變量所有刪除。--可是在這裏有個疑問:究竟怎麼定義內存泄漏,要是用的內存、變量還叫作泄漏嗎?
(2)閉包會在父函數外部,改變父函數內部變量的值。因此,若是你把父函數看成對象(object)使用,把閉包看成它的公用方法(Public Method),把內部變量看成它的私有屬性(private value),這時必定要當心,不要隨便改變父函數內部變量的值。
六:一點延伸:什麼是函數申明、函數表達式
function fnName () {… }; var fnName = function () { …};
函數聲明:function fnName () {…}; 使用function關鍵字聲明一個函數,再指定一個函數名,叫函數聲明。
函數表達式: var fnName = function () {…}; 使用function關鍵字聲明一個函數,但未給函數命名,最後將匿名函數賦予一個變量,叫函數表達式,這是最多見的函數表達式語法形式。
匿名函數:function () {}; 使用function關鍵字聲明一個函數,但未給函數命名,因此叫匿名函數,匿名函數屬於函數表達式,匿名函數有不少做用,賦予一個變量則建立函數,賦予一個事件則成爲事件處理程序或建立閉包等等。
函數聲明和函數表達式不一樣之處在於,1、Javascript引擎在解析javascript代碼時會‘函數聲明提高’(Function declaration Hoisting)當前執行環境(做用域)上的函數聲明,而函數表達式必須等到Javascirtp引擎執行到它所在行時,纔會從上而下一行一行地解析函數表達式,2、函數表達式後面能夠加括號當即調用該函數,函數聲明不能夠,只能以fnName()形式調用
總結:函數聲明提高是重點,能理解了這個概念就懂了聲明和表達式的區別了。
七:關於此次分享的緣由
引起此次技術學習的一段代碼我貼出來
var Eplus365Verifiy = (function (self) { self.FrameworkName = 'Eplus365Verifiy.js'; self.FrameworkVersion = '1.0.0'; //手機號的正則驗證 self.MobileVerifiy = function (val) { var myreg = /^1[3,4,5,7,8]\d{9}$/; if (myreg.test(val)) { return true; } return false; } //長度驗證,區分中文和字符-若是數據類型設置爲varchar才須要這方法驗證,若是是nvarchar就不要用了 self.LengthVerifiy = function (val, length) { var len = getStringLen(val); if (len > length) { return false; } else { return true; } } return self; } (Eplus365Verifiy || {})); var Eplus365Verifiy = (function (self) { self.ss = "ss"; return self; } (Eplus365Verifiy || {})) console.log(Eplus365Verifiy.MobileVerifiy("15235382691")); console.log(Eplus365Verifiy.ss);
這裏須要理解的是
1:匿名自執行函數
2:Eplus365Verifiy || {} 沒有值得時候的初始化
3:return self ;//把自己做爲一個返回值,而不是方法。
4:兩次定義,可是給贊成對象。
總結:
此次關於閉包的技術分享,其實更多的是關於js基礎知識點:變量做用域、鏈式做用域、垃圾回收機制、函數聲明、函數表達式、匿名自執行函數等等概念的理解,閉包是一個知識點,很簡單但也不是很好理解,但願對你們有幫助吧。
此次學習參考了不少技術大牛的經驗,再次鳴謝。