若是你還沒忘的話,仔細回想一下,以前咱們是如何將對象綁定到變量名上的。但當時咱們只是全局綁定,在那時這種綁定是很是有用的。不過,有不少時候,本地綁定每每比全局綁定更合適,例如把變量限制在一個操做內部的時候。下面就讓咱們看看若是使用綁定函數 "let " 進行本地綁定。 javascript
=>id java.lang.Exception: Unable to resolve symbol: id... =>(let [id 1] (println id)) 1 nil =>id java.lang.Exception: Unable to resolve symbol: id...正向你看到的南陽,經過使用"let" 操做,咱們把1綁定到了」id「這個變量名上。而後咱們又把它打印了出來。當這個操做執行完後,咱們在外面查看」id「時倒是沒法解析的。這就證實了變量」id「只存在於操做內部(本地綁定相似於java中的方法局部變量)。
本地綁定能夠將變量限制在某個操縱內,這樣就不會形成對其餘操做的變量污染。試想一下,若是沒有本地綁定,一旦咱們使用了id這個變量名後,咱們就不再能使用它來綁定其餘對象了。使用本地綁定後,咱們能夠在某個操做內使用任何有意義的變量名而不用擔憂和其餘相同名字的變量形成衝突,即便是全局變量: java
=>(def id 0) ;;全局綁定 #'user/id =>id 0 =>(let [id 1] ;;本地綁定 (println id)) 1 nil =>id ;;本地綁定對全局綁定沒有任何影響 0
這種行爲一般被稱爲詞法做用域,咱們能夠保護變量不受污染,甚至是父操做也存在一樣的變量名: 函數
=>(let [id 1] ;;外層操做 (let [id 2] ;;內層操做 (println id)) ;;內層本地綁定的id (println id)) ;;外層本地綁定的id 2 1 nil
咱們再來舉一個例子。上一篇最後咱們寫了一個」data-list"函數,這個函數最終返回給咱們一個包含各個時間元素的列表。咱們每調用一次,它都會返回給咱們當前時間年、月、日、時、分、秒組成的一個列表: .net
=>(date-list) ("2013" "07" "10" "15" "02" "59") =>(date-list) ("2013" "07" "10" "15" "03" "02")
如今呢,咱們想要這麼一個函數 run-report,經過這個函數,咱們能只打印出"時"和"分"這兩個元素。這個簡單咱們能夠向下面這樣去實現它: code
=>(defn run-report [] (str "report ran: " (nth (date-list) 3) ":" (nth (date-list) 4))) #'user/run-report =>(run-report) "report ran: 15:04"
上面這個函數有什麼問題嗎?聰明的你就會發現data-list這個函數被調用了兩次。一次咱們用來獲取小時,一次咱們用來獲取分鐘。這樣作的話有兩個壞處。第一個是,這兩次調用返回的時間是不同的(函數在快也須要時間執行),咱們極可能獲得很是錯誤的結果。假如第一次調用剛好是15:59:59,到了接近16點的臨界點。第二次調用變成了16:00:00。這兩個組合就變成了 15:00 。第二個壞處就是,排除第一個錯誤的話,若是data-list執行時間比較長,屢次調用勢必影響函數效率。 對象
更好的作法就是咱們只調用一次data-list,而後把調用後的結果綁定到一個本地變量上: blog
=>(defn run-report [ ] (let [date (date-list)] ;;data-list是全局變量。date是本地變量 (str "report ran " (nth date 3) ":" (nth date 4)))) #'user/run-report =>(run-report) "report ran: 15:09"經過上面的作法,以前的兩個問題都不復存在了。
下面是另外一種作法: ip
=>(defn run-report [date] (str "report ran: " (nth date 3) ":" (nth date 4))) #'user/run-report =>(run-report (date-list)) ;; 傳入參數就是一種隱式的本地綁定 "report ran: 15:10"咱們給run-raport 函數添加了一個參數date,參數對函數來講就是一個隱式的本地綁定。當函數被執行時,用實參替換形參的時候,本地綁定就自動的和隱式的進行了。
關於詞法做用域能夠參考這篇文章,雖然是關於javascript的,但道理是同樣的。 作用域