js閉包vs Java內部類
前言:
昨天寫了一個關於Java內部的部落格,在內部類的最後一點中談到了Java閉包的概念,他是這樣定義閉包的: 閉包是一個可呼叫的物件,它記錄了一些資訊,這些資訊來自建立它的作用域。結合Java的內部類可以很好的理解這一點(如有需要可參考ThinkInJava之內部類)。 突然之間想到js中的閉包,一直都無法很好的理解,故藉此又看了一下js中的閉包,對我個人而言,感悟良多,藉此也與大家分享一下,希望可以幫助大家,並一起快樂的學習成長,天天向上。
零:js閉包概念(通過Java閉包和js巢狀函式和圖一分析 :純個人 見解,歡迎評論和建議)
js:閉包是一個返回給呼叫者的物件,而這個返回物件攜帶了一些呼叫者無法獲取的資訊
一: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>
二:js中的變數作用域(全域性變數和區域性變數)
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
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成員的問題請看下面
四 :JS的 內嵌函式(類比Java內部類 ,我們發現巢狀函式及其的像Java的內部類:joy:)
JavaScript 支援巢狀函式。巢狀函式可以訪問上一層的函式變數(我們可以這樣理解:內部的變數可以訪問其外面的變數,而外面的不能訪問內部的。
//一個簡單的js巢狀函式 function add() { //外部類 var counter = 0; function plus() { //巢狀函式內部類 counter += 1; //巢狀函式可以訪問其外部的變數 } plus(); return counter; }
類比Java :Java內部類可以訪問外圍物件的所有屬性包括私有屬性,js的巢狀函式好行也有這個屬性:smile:。
五: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方法也是物件
六: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 : 並且以'()'括號結尾
如果滿足以上兩個條件,沒有特殊原因都會稱為立刻執行函式。
***************************************歡迎讀者給出建議***********************************