[JS]scope --- (1)
本篇醞釀的時機灰常的微妙,我親愛的學姐~ > ////< 也(因為五斗米)踏入了JS界了~。
很多的書籍在介紹js的時候、除了一開始的超經典hello world,接著就會開始介紹browser的歷史,然後、什摸什摸的分野
,接著就是ㄜ…什摸沒有型態又有型態的弱型態特性,然後bind event,接著有些也許會深入介紹一下經典特性、例子什
摸的。可是、其實我覺得在弄完hello world後、應該是要先講scope和closure~ XDDD 那可以讓好多人少繞很多彎路。
因為、以前找資料,前輩們說起js時、總是會…『這就是因為scope呀~』『阿這個就是用那個closure喇~』
『這個bug就是因為scope,balabala…』
在剛學JS的時候,這2個名詞對我來說、真的好疑惑好神秘,不懂耶~問了很多次…還是一知半解~ Q 口Q 真的很失敗。
所以、scope…. 其實是我很一開始的時候就想好好記錄下來的一個topic,因為、它對我有很深的意義。是我一開始接觸
JS時,遇到的第一個從完全不知道怎摸下手測試與trace,只好呆呆的硬背下來。也因為正式的面對它、讓我真正邁入JS的
門檻。這..難道就是所謂的、成也蕭何、敗也蕭何 (大誤)
其實、這世界上有很多講解scope的文章和書籍,我也陸陸續續看過很多篇,不過、也許是我不夠聰明、或者和前輩們的觀點
不太一樣,在我trace的過程、總覺得…有些細節怪怪的。不過、我也不敢保證我的說法一定是對的啦~(汗) 也許、這個東
西講起來會有點點抽象,不過、就想做一個紀錄。
一開始、說到scope,我們對於不熟悉的語言都會有點點害怕… 所以、把它翻成中文就是…『作用域』
講到作用域、就很熟了~ = . .= 對、我熟悉的作用域、是數學的作用域~ XDDD 不過、那又怎樣…起碼它給我靈感了。
所謂的『作用域』就是在這個範圍內,是有用的、有意義的。
所以、在有了方向後,接著來看下面這2個框框的東西:
如果、我們在作用域1定義了a、在作用域2也定義了a,還蠻明顯的,有在寫程式的人應該可以區分出來,在作
用域2中的a和作用域1的a明顯的不同。
(作用域1)
var a (<—視為globle)
(作用域2)
var a (<—視為local)
一樣、我們在作用域1定義a,不過我們在作用域2沒有定義a,可是一樣使用a,那…很明顯的,有在寫程式的
人,也一定可以區分出在作用域2的a就是作用域1的a(作用域1) var <strong>a</strong> (<---視為globle) <div style="border:solid red 2px; width: 230px; height: 100px; text-align:left;"> (作用域2) <strong color="red">a</strong> (<---local找不到,往上層...發現a,所以,這個a是globle的a) </div>
然後、我自己後來覺得有個比喻很棒耶~ @ @++
就把function當作籠子、變數們當作小動物…關起來、這樣這樣、然後再那樣那樣~ w
好吧、說穿了~ = . .= 我覺得這就是所謂的scope。可是、為什摸還是有很多人敗在scope下,被它耍的團團轉咧~!!
其實、原因也很簡單,歸納如下:
- 大家其實說scope、說得很順…卻從來沒有認真的搞清楚過JS的作用域的定義。
JS是functional language,大部分的人都會說、在寫程式的時候,並沒有把function視為一個領域看待,特別是先學
過其他非functional language**的人來說,在最一開始、應該都會把花括號當作一個scope、因此就混亂了。 - 因為它沒有強制定義型別,JS的變數都可以塞任何型別進去,在交互作用下,早就忘記abcd到底塞了些啥進去了。
- 當尼的code落落長的時候、慘慘…跑到最上面看不到整個運作、跑到最下面看不到定義… = “””= 更不用說,花括號
亂亂長,縮排縮到不知道哪兒去了。 - 不良的寫作習慣。
其實、每種語言的風格都差蠻多的啦、以我來說~ = “””= 真的、都差很多~ w 以前、我也會想,我想要把functional
language搞得很像oo language,後來…想把oo language搞得像functional language~ = . .= 是可以啦~ 不過、我覺得
那是自討苦吃~ XDD 應該這摸說,可以沿用它的精神,也就是…神似而型不似,這是沒有問題的,而不推薦要整個都長得
很像(從定義到實做)。
##作用域 之 初學幼幼班
在最前面、看過作用域的基本定義後,一定要來現學現賣,練習一下…所以、來看小小2段程式。
- 範例1
- 流程:
- 在最外面定義變數 a 和function b。
- function b中、給予a一個新的value。
- 依照順序把a給log出來看。
- 說明:
- 第一個log:毫無疑問的,因為在最一開始定義了a,遇到第一個之前並沒有任何的操作會影響,因此、肯定是123。
- 第二個log:執行b function,同樣的、在b裡面,a被影響到了,應該也不會遲疑a=456這個答案。
- 有疑問的第三個log來了:前面有提過、JS的作用域的基本單位是function,所以、在function內有定義的、會
和外面不同、不然就是同一個。而在b中,我們的a並沒有另外定義,因此、b內的變數a就是外面的那個a。然後、
因為在b裡面已經被改過了,所以、第三個log的結果就會是… 456囉~
- 流程:
|
|
這個、和上面那個差不多同樣的方法推演喇~ = . .= 就跳過了~ w
|
|
##作用域 之 超常見陷阱題
嗯嗯、這一段的程式,相信是大家很常見的一個類型,然後… 大家的答案近乎一致性的都是『阿這就是scope(closure)~』
小的駑鈍~ = “””= 當時的我沒有辦法用這種話就可以明白。現在的我、用我所理解的來解釋一下。
範例3:
流程:相信、有些許程式經驗的人,一般來說,都會覺得、下面是沒有什摸問題的code,就是把function塞進去空
陣列a,然後…再接著執行陣列a的每一個function,然後… function只是很單純的把index給log出來。是~~~~這摸想的對吧~ = . .=
沒錯、初學JS,而又有一點基礎的人,十之八九都會這摸寫。因為、在我們心裡,潛藏著花括號就是作用域的刻板印象。
而…這就是造成,為什摸大家會說『js很難懂』『js很怪』『js根本就不是人學的』『js很討厭』『js…balabala』的
原因。解析:在解析前,請大家認真的把function當作個柵欄,把變數們當作小動物…關起來(圍毆…XDD!?)。
- 首先、我們這一段code,作用域1。
- 接著定義了空陣列a。
- 用一個loop定義了…3個小作用域。且、小作用域的i…沒有使用定義,因此…參考至外部的…i。因此、i就會
隨著loop的i改變而改變。(←注意到了嗎? 這就是,人家說的… = . .= js的陷阱) 執行塞到a陣列的function。
執行階段:根據上述的解析,在執行a陣列的各個function的時候,此時的i=3 (←這個道理、應該…不用詳述了吧~
= . .=)、而小function的i沿用外部的定義,因此….這個i,log出來不等於3要等於啥摸咧~ 懂了吧、懂了吧~
就就就是降~~~~ w
|
|
- 範例4:那…如果想達到原本上面我們所想的,該怎摸做咧~看接下來的這個就對了~ w
- 解析:
- 同樣的定義空陣列a。
- 一樣遇到loop了,為了解決因為作用域引起的問題,也就是說…我們只要讓function裡面的i可以跟外面的i有所區別
就可以解決了對吧~ 0 因此在這邊、我們在原本的function在包上一層。也就是說,我們使用了一個馬上執行的匿名
function,把i塞進去。這裡花幾個步驟,稍微解釋一下囉~ w- 所謂的匿名function,白話的說、就是,這個function是個沒有名字的function,除了沒名字以外,跟一般
的function都一樣。所以、我們可以得到如下的訊息:- function是個作用域。
- function的參數,是有被定義的。
- 根據上面2點、我們可以知道,有被參數定義過的,會和外層同樣名字的意義是不一樣的。
- 因此、根據上面那點,再更詳細的描述一下,這一小段的意義。我們要的是真正想要的是return回來的
function那一段,而為了i,我們幫它包上一層function、以隔絕掉內外的差別,而這個i必須給予定義,
如果、不以參數的形式,那…就沒意義了,因此、帶入參數…。- 最後把a陣列的function都執行一次出來看… 歐耶~結果對了~ w
- 所謂的匿名function,白話的說、就是,這個function是個沒有名字的function,除了沒名字以外,跟一般
- 解析:
|
|
聰明的尼,一定會想說… 阿~ i來i去的~ = . .= 匿名function的參數我可不可以不用i… 可~~以,來對照一下吧
|
|
##總結
其實、因為js定義範圍的基本單位是function,因此、有些寫法,也許會讓人覺得多餘…,可是,其實我覺得很有道理耶~
比起會被block住的其他語言來說,其實…functional language更能說服我,我覺得…那很直覺、很蘇湖的想法~ @ 3@
嗯嗯、如果尼對scope很熟悉…那真的是太好了~ 我一點都幫不上忙~(攤手)
如果、尼是scope苦手,那…請試著用我有點點彆腳、又需要一點點想像力的描述~ (Q 口Q 我很努力了~)
看看自己的code、別人的code…。當尼越來越熟悉這樣的方式時,尼會發現,好多js的code都隱藏了不少的bug(大誤)
本篇、也將開啟一個系列文~ Q 口Q 怎摸辦、我又挖坑了。為什摸咧~因為、有太多的東西…都跟這個作用域有關西,
什摸memory leak、recursive、tail recursive…一堆堆balabala很難懂的、隱藏性bug的地方。其實、說穿了都沒啥~ w
藉此、也想強迫自己認真的用自己的話,為自己的頓悟下個註記。