對于一個在前端屬于純新手的我來說,Javascript都還是一知半解,要想直接上手angular JS,遇到的阻力還真是不少。不過我相信,只要下功夫,即使是反人類的設計也不是什么大的問題。
Okay,廢話不多說。為了弄明白angular JS為何物,我先是從Scope開始。那么什么是Scope呢?借用官方文檔的一段話:
看完后,類比到其他的編程語言上,感覺Scope就像是Data Model的作用域一樣,為Expressions的執行提供上下文,暫且先這么理解吧。
Scope的特性
接下來,看看Scope有哪些特性呢?
Scope提供$watch方法監視Model的變化。
Scope提供$apply方法傳播Model的變化。
Scope可以繼承,用來隔離不同的application components和屬性訪問權限。
Scope為Expressions的計算提供上下文。
對于這四點特性,因為我之前學習過ActionScript、C++、Java,所以第一、三、四點不難理解,唯獨第二點感覺有點云里霧里。本著打破沙鍋問到底的原則,我還是通過Google搜到了一些東西。對于有經驗的老手,板磚請輕拍!
源起Javascript
首先,乍一看,scope.apply()似乎就是一個使得bindings得到更新的普普通通的方法。但稍微多想一點,為什么我們需要它?一般在什么時候用它呢?用弄明白這兩個問題,還得從Javascript說起。在Javascript代碼里,都是按照一定順序來執行的,當輪到一個代碼片段執行的時候,瀏覽器就只會去執行當前的片段,不會做任何其他的事情。所以有時候一些做得不是很好的網頁,當點擊了某個東西之后會卡住,Javascript的工作方式就是會導致這一現象原因之一!下面我們有一段代碼來感受一下:
當加載Javascript代碼時,先先找一個一個id叫“clickMe”的按鈕,然后添加一個監聽器,然后設置超時。等待5秒,會彈出一個對話框。如果刷新頁面并立即點擊clickMe按鈕,會彈出一個對話框,如果你不點擊OK,timerComplete函數永遠沒有機會執行。
如何更新bindings
好了,扯了一些看似不相關的東西之后,我們回歸正題。angular JS是怎么知道什么時候數據的變化和頁面需要更新的呢?代碼需要知道什么時候數據被修改了,但是現在又沒有一種方法直接去通知說某個對象上的數據變了(盡管ECMAScript 5正在嘗試解決這一問題,但也還是處于實驗階段)。而目前比較主流的策略有以下有兩種解決方案。一種是需要用特殊的對象,讓所有的數據都只能通過調用對象的方法設置,而不是直接通過property指定。這樣的話,所有的修改就可以被記錄下來了,就知道什么時候頁面需要更新了。這樣做的弊端就是我們必須去繼承一個特殊的對象。對于賦值也只能通過object.set('key', 'value')而不是object.key=value的方式。在框架中,像EmberJS和KnockoutJS就是這么干的(雖然我都沒接觸過——囧)。另一種就是angular JS采用的方式,在每一次Javascript代碼執行序列執行結束后都去檢查是否有數據的改變。這看起來似乎并不高效,甚至嚴重影響性能。但是angular JS采用了一些比較巧妙的手段解決了這個問題(還沒研究過,目前尚不明確)。這么做的好處就是,我們可以隨便使用任意對象,對于賦值方式也沒有限制,而且對于數據的改變也能覺察到。
對于angular JS采取的這種解決方案,我們關心的是什么時候數據發生了變化,而這也正是scope.apply()派上用場的地方。對于檢查綁定的數據到底有沒有發生變化,實際上是由scope.digest()完成的,但是我們幾乎從來就沒有直接調用過這個方法,而是調用scope.apply()方法,是因為在scope.apply()方法里面,它會去調用scope.digest()方法。scope.apply()方法帶一個函數或者一個表達式,然后執行它,最后調用scope.digest()方法去更新bindings或者watchers。
什么時候用$apply()
還是那個問題,那我們到底什么時候需要去調用apply()方法呢?情況非常少,實際上幾乎我們所有的代碼都包在scope.apply()里面,像ng−click,controller的初始化,http的回調函數等。在這些情況下,我們不需要自己調用,實際上我們也不能自己調用,否則在apply()方法里面再調用apply()方法會拋出錯誤。如果我們需要在一個新的執行序列中運行代碼時才真正需要用到它,而且當且僅當這個新的執行序列不是被angular JS的庫的方法創建的,這個時候我們需要將代碼用scope.apply()包起來。下面用一個例子解釋:
上面的代碼執行后頁面上會顯示:Waiting 2000ms for update。顯然數據的更新沒有被angular JS覺察到。
接下來,我們將Javascript的代碼稍作修改,用scope.apply()包起來。
這次與之前不同的是,頁面上先會顯示:Waiting 2000ms for update,等待2秒后內容會被更改為:Timeout called! 。顯然數據的更新被angular JS覺察到了。
NOTE:我們不應該這樣做,而是用angular JS提供的timeout方法,這樣它就會被自動用apply方法包起來了。
科學是把雙刃劍
最后,我們再來瞅一眼scope.apply()和scope.apply(function)方法吧!雖然angular JS為我們做了很多事情,但是我們也因此丟失了一些機會。從下面的偽代碼一看便知:
它會捕獲所有的異常并且不會再拋出來,最后都會調用$digest()方法。
總結一下
$apply()方法可以在angular框架之外執行angular JS的表達式,例如:DOM事件、setTimeout、XHR或其他第三方的庫。這僅僅是個開始,水還有很深,歡迎大家一起來deep dive!
新聞熱點
疑難解答
圖片精選