前言:html
昨天寫了一個關於Java內部的博客,在內部類的最後一點中談到了Java閉包的概念,他是這樣定義閉包的:閉包是一個可調用的對象,它記錄了一些信息,這些信息來自建立它的做用域。結合Java的內部類能夠很好的理解這一點(若有須要可參考ThinkInJava以內部類)。忽然之間想到js中的閉包,一直都沒法很好的理解,故藉此又看了一下js中的閉包,對我我的而言,感悟良多,藉此也與你們分享一下,但願能夠幫助你們,並一塊兒快樂的學習成長,每天向上。java
js:閉包是一個返回給調用者的對象,而這個返回對象攜帶了一些調用者沒法獲取的信息安全
<script> function Car() { //定義class var color = "blue"; //定義屬性 } Car.prototype.getColor = function() { //經過原型定義方法 console.info(this.color) return this.color; } var oCar1 = new Car();//建立對象實例 oCar1.getColor(); //調用方法 </script>
var a = "我是全局變量"; function myFunction() { var b = "我是局部變量" return a ; }
function my2(){
consoke.info(b) #報錯
}
2.1 全局變量a:即屬性window的屬性,在同一頁面內全部的js腳本,都共享同一個window對象,故共享全局變量a.閉包
2.2 局部變量b :局部變量只能用於定義它函數內部。對於其餘的函數或腳本代碼是不可用的。函數
備註:變量聲明時若是不使用 var 關鍵字,那麼它就是一個全局變量,即使它在函數內定義。學習
解題思路:須要一個變量,這個變量須要在方法內訪問並加一,屢次調用該變量就是屢次加一的和,故不能把該變量定義在方法的內部,若是把該變量定義在方法的內部就不能實現屢次調用返回屢次調用的和,故把該方法定義爲全局變量以下,但這樣定義該變量即不安全如調用方法2 this
var counter = 0; function add() { return counter += 1; } function myFunction(){ document.getElementById("demo").innerHTML = add(); } function myFunction2(){ counter = 100; document.getElementById("demo").innerHTML = add(); } myFunction(); myFunction();//實現屢次調用返回,屢次調用的和 ##counter =2 myFunction2();//但若是調用該方法,就不返回屢次調用的和 #由於是全局變量,任何腳本均可更改該變量的值,這樣及其不不安全。 ##counter =101
解題思路2:若是能把count變量隱藏起來不讓其它js方法修改它不就好了嗎?若是咱們熟悉Java語言,用Java就很容易解決該問題。由於Java提供的修飾符private能夠控制屬性的訪問限制。並定義一個惟一public方法設置該屬性(就是把屬性定義爲私有的,並提供惟一的get和set方法,就這麼簡單)。然而若是把問題拋給js就很難解決這個問題了,覺得js沒有提供這樣的修飾符,來控制訪問屬性。如何解決相似Java private成員的問題請看下面spa
JavaScript 支持嵌套函數。嵌套函數能夠訪問上一層的函數變量(咱們能夠這樣理解:內部的變量能夠訪問其外面的變量,而外面的不能訪問內部的。prototype
//一個簡單的js嵌套函數 function add() { //外部類 var counter = 0; function plus() { //嵌套函數 內部類 counter += 1; //嵌套函數能夠訪問其外部的變量 } plus(); return counter; }
類比Java :Java內部類能夠訪問外圍對象的全部屬性包括私有屬性,js的嵌套函數好行也有這個屬性😄。設計
var aa = ( function(){ alert("sssss"); return {}; } )() //備註:當咱們刷新當前頁面時就會執行function方法並返回{}對象 //不須要手動調用該方法
當即執行函數的寫法有不少中以上是最經常使用的方式()(),下面也是當即函數的寫法
1 !function foo(){...}();
2 +function foo(){...}();
經過馬上執行函數,而且返回一個空的對象,結合內嵌函數的特性,咱們可知這個對象是能夠訪問外圍的屬性和方法的。
而後咱們分析:利用嵌套函數和閉包的特性來分析下圖
{//區域A(window) {//區域B return {//區域C 區域C返回到了區域A } } }
圖一
結論以下
1. 區域A不能訪問區域B定義的數據,故B就對A隱藏了。然而區域C能夠訪問區域B定義的數據,
2.區域C同過return返回給了區別A,若是區域C是一個方法,則A就能夠調用這個方法,而這個方法是惟一能訪問到B區域的(B提供了一個public方法共全局訪問)。故B又提供了對A訪問的方法。
這樣就造成了一個js的閉包:(即閉包是一個返回給調用者的對象,而這個返回對象攜帶了一些調用者沒法獲取的信息)
備註:js方法也是對象
代碼以下
var add = (function () { var counter = 0; return function () {return counter += 1;} })(); add(); add(); add(); // 計數器爲 3
備註1:js函數分類
函數聲明:function fname(){...}; 使用function關鍵字聲明一個函數,再指定一個函數名。
函數表達式:var fname=function(){...}; 使用function關鍵字聲明一個函數,但未給函數命名,最後將匿名函數賦予給一個變量。
匿名函數:function(){}; 使用function關鍵字聲明一個函數,但未給函數命名。(匿名函數也屬於函數表達式。)
備註2:js解析機制
js:解析機制:分爲編譯和執行兩個階段。
先編譯:有人也稱預編譯,js解析器會掃描整個js文件,把以var (定義變量)和function(定義方法)開頭語句作變量提高。
執行 :給定義的變量賦值,或執行相關方法(以括號‘()’結尾的語句,會看成方法來執行)
利用js解析機制來來回答爲何函數會自我調用
在執行階段,js解析器發現該函數 1 : 沒有以var或function開頭。
2 : 而且以'()'括號結尾
若是知足以上兩個條件,沒有特殊緣由都會稱爲馬上執行函數。
備註3: js當即執行函數 vs Java單例模式
若是屬性java的讀者必定接觸過單例模式:即一個應用系統中只容許擁有一個惟一的某個類型的實例對象,具體寫法再次就很少介紹了。經過分析和觀察當即執行函數,由於該函數的執行是在js解析器加載的時候執行的(能夠類比爲Java應用程序啓動時加載單例),而且很難手動再次加載它(我是沒有發現方法😄),故咱們能夠理解當即函數就是單例設計模型(沒有研究過js的設計模型,也不知道有沒有😄)。
***************************************歡迎讀者給出建議***********************************