2016年8月29日

為什麼只做過一次實驗的科學研究,我們不該全然相信?

Photo credit by Dino Reichmuth CC0

隨著教育水準的提高,越來越多民眾開始對於科學文章已經能夠用更謹慎不輕易相信的態度來檢視,同時也能對於毫無根據的說法提出質疑加以反駁,甚至上網搜尋更多的資料加以求證。

然而,實際在驗證這些資訊的過程中,往往又會碰到另一個難題,以手機會不會致癌為例,在蒐集資料的時候,我們會找到證據指出「低頭族注意!美證實手機電磁波會致癌」,在詳細閱讀發現是可靠的資訊來源和實驗研究後,正當我們打算以此為結論,和其他人說明手機對於癌症的影響時,卻又突然找到另一篇同樣打著科學研究的結果聲明「最新研究:使用手機不會致癌」。

這種矛盾的情況經常發生,同樣都是科學研究卻有截然不同的結果,往往讓一般民眾無所適從,更讓想要找出客觀證據的鍵盤柯南們更不知道該相信誰。這到底是怎麼一回事呢?是科學家、媒體的問題?還是科學本質上的問題?我們又該如何看待各式各樣的科學新聞?該不該相信這些研究結果?

在這裡,我們不打算探討科學方法當中該研究是否具可證偽性、是否具備良好的操作型定義、是否考慮了相關不等同於因果關係等問題。我們先假設,這些媒體所報導的研究結果在研究方法上已經通過同儕的檢驗,也發表於相應聲望良好的期刊,沒有任何實驗和研究方法上的問題。然而,即使是完美實驗設計下所發現的研究結果,你也不應該完全相信它?因為它們大部分都只是單一的研究結果。

科學研究的重點——能否重複驗證


這裡我們要回到科學研究中很重要的一點——是否可被重複驗證。可被重複驗證指的是,其他科學家透過同樣的研究方法、實驗方式,將可以得到一樣的研究結果。一個實驗的研究結果即使對人類社會具有極大的意義或貢獻,一旦沒有辦法被其他人所重複驗證的話,也沒有辦法在科學界立足,因為這樣的結果很可能反映的只是意外或巧合所產生的結果。

舉個例子來說,你的鄰居今天運動完後在投幣機投飲料,這時候他靈機一動,冒出了一個想法「會不會重複點飲料的按扭,機器就會感應到很多次,進而掉出很多罐飲料來」,於是他就在投幣後,重複按了很多次點選飲料的按鈕,沒想到結果居然如他所料,一次掉了10瓶飲料出來。欣喜若狂的他跑回家跟親朋好友分享這個消息,告訴大家說:「我有一個驚人的發現,只要在投飲料的時候一直按該飲料的按鈕,就會掉出很多罐飲料來。」大家聽到之後,都非常興奮,於是又去告訴更多的人,就這樣這個消息很快的傳遍了整個社區。但很快的,越來越多人實際去投幣機嘗試後,發現這樣的作法根本沒效,那次之所以掉出很多飲料來,只是剛好機器故障的巧合罷了。

看出這其中的問題了嗎?單一科學研究結果的發現有些時候就有可能是巧合,就像鄰居的發現一樣,不代表就是真實或最後的結論。

發達通訊技術擾亂了知識傳播


在過去通訊設備和資訊傳播仍不像當今這麼發達的時代,科學家們當然一樣也會有些重大發現,只是這些發現不會這麼快的進入到民眾的資訊圈中,當有科學家提出某些研究結果後,對這樣結果有質疑的其他科學家們,便會去進行相同的實驗,看看能不能驗證出相同的實驗結果。在眾多科學家共同努力的情況下,我們慢慢可以得出某些理論是不是更靠近事實。這樣一個又一個的研究累積,是導引科學進步很大的動力。

也就是說,過去科學是先有實驗結果,並且在科學界裡經過了重覆驗證、檢驗的過程,累積了一段時間,成為比較可靠的知識後,才會傳播到人群上來。科學不是一篇研究結果就可以成為定理,提出一個結果之後會被其他的科學家質疑,甚至提出了相反的研究結果,經過長時間的驗證無誤後,才會變成一個可被接受的理論。

然而,在資訊傳播快速、媒體發達的今天,只要有一個新的研究結果產生,就會立刻傳入民眾的生活圈中,科學知識常常尚未經過重覆驗證和檢驗的階段就直接進入了人群知識當中,這些研究結果雖然是以科學方法進行,卻仍不一定是最終的結論。

這些以科學方法所得到的結果,實際上在科學界常常仍結論未定,但卻因為它常是驚人的、有趣的、令人振奮的,進而在媒體或書籍中被大量的刊登引用,這些單一研究結果在重覆的曝光之下,常常被當成了正確無誤的知識。後來即使科學家們發現這樣的結果並沒辦法得到重複驗證,媒體或許早已對這個議題沒有那麼大的興趣,而錯誤的結果也就這樣被人們所相信著。這樣的情形最常出現在和人有關的醫學或心理學的研究上,但實際上,和人有關的研究卻往往又是最難找到定論的。

單一研究結果的不可信之處


我們都知道在各式各樣的廣告中,最有影響力的往往是某某人的使用經驗分享。這種故事型的、使用者案例的模式往往是最有說服力的論點。想想,賣減重藥的藥商如果跟你說,這個藥吃了之後,35%的人會瘦1公斤、25%的人瘦2公斤、20%的人沒有效果、15%的人變重了1公斤,相信你一定不會想要買這樣的藥。但藥商如果告訴你說,隔壁鄰居李小姐也有來買這顆藥,你看她吃了之後不到幾個月身材變得更苗條,小腹都不見了,你一聽到這裡,回想到鄰居李小姐的身材確實變瘦了,立馬就掏出荷包來希望能夠和她一樣減重成功。

就是因為單一研究結果在論述上非常具有吸引力和說服力,再加上科學方法的加持,很多人都認為透過科學方法得到的研究結果一定可信,所以很多的新聞或書籍也都喜歡引用這樣單一的研究結果當作具體的論點來吸引大家的注意。這麼做其實並沒有不對,站在作者的角度看,他為了讓大家接受他的論述,進而使用這樣的寫作方式;但身為讀者的我們,應該更審慎的看待這些引用的資料,即使打著「某某科學研究結果發現⋯⋯」或者「某某科學家找了28個男性、22個女性,將他們分配到實驗組和控制組,發現到⋯⋯」這樣的論述仍不一定是正確可信的結論。

除了上述所提,我們對於單一研究結果不可全然相信外,大家可能都看過綜藝節目中類似的傳話遊戲,第一個人聽完題目之後往後傳,最後一個人只要把他所聽到的答案念出來就可以,在傳遞的過程則會穿插外國藝人,或者戴上播放大音量的耳機來增加遊戲的困難度。我們可以看到訊息在傳遞的過程中,是怎麼將原本的訊息一次又一次的轉化、扭曲,最終變成了四不像的結果,答題者最後則往往透過所獲得的有限資訊和自己的腦補將答案拼湊出來。

你分享的科學新聞正確嗎?


科學新聞的傳遞常常也像綜藝節目般,除了訊息本身在轉傳時就可能發生錯誤外,在加上語言的差異性、新聞的時效性,有些時候記者可能只讀了原始文章的標題、摘要就撰寫出一篇新聞稿來,或者是透過外國媒體的報導,將所取得的第三手資訊轉發,就在這樣訊息傳遞的過程當中,錯誤解讀原始資訊的情形相當常見。除此之外,許多科學新聞報導為了吸引民眾目光常會將標題撰寫的相當聳動、引起焦慮、有趣、或者充滿希望的感覺,藉此更提高該文章點擊率和社群分享數。這些也都是我們在閱讀這些科學新聞時,需要特別留意的部分。

在這裡,我們並非指稱科學知識或科學方法不可信,事實上,它仍然算是我們逼近真理的最好方法;然而,有問題的是,認為透過科學方法所產生的單一研究結果都是正確無誤的知識,而忽略了科學很重要的累積、重覆再驗證的過程。

科學新聞之所以能夠在社會媒體上快速傳播,常常因為這些新聞能夠引發人們的情緒,這些科學新聞經常是有趣的、驚人的、帶給人希望的、讓人絕望的。因此,在面對這些科學新知,我們除了應該先用原有的知識來判斷這個新聞的可信度外,更重要的是,如果是單一研究結果,我們更應該對於這樣的結果保持保留的態度,不要輕易地掉入了非黑即白,不是正面就是反面的二分思考模式當中,多用機率的角度看待各式各樣的研究結果或結論。我們可以把這樣的結果當成一個假設,繼續追蹤類似的消息,同時留待更多科學家協助我們對該結論進行重複檢驗。



本文章同步刊登於 thenewslens 關鍵評論<為什麼只做過一次實驗的科學研究,我們不該全然相信?

2016年8月9日

[心得] 如何成為一名優秀的設計師:羅子雄



每當我們看到一些美妙設計的時候,
很多人心裡面會有一種衝動,
這種衝動會讓你們想去創造一些東西,
創造一些美妙的事物,
這些東西讓你想成為一名設計師
─ 羅子雄@TEDxChongqing。


這是錘子科技設計總監羅子雄 2015 年在重慶的演講〈如何成為一名優秀的設計師〉,最近在 FB 上的曝光度非常高,短短的18分鐘,非常推薦,在這裡和大家分享個人聽完後的筆記和心得。

蛻變


學習設計是一個不斷進步的過程,我們沒辦法一蹴可及,沒辦法一下子就要求自己達到完美,更實際的是,你所認為的完美和別人所認為的完美可能不同;現在你所認為的完美,和幾年後你所認為的完美可能也不一樣,所以我們要追求的是持續的進步和成長

羅子雄和大家分享,想要成為一名優秀設計師的三個重點 ─ 看‧做‧想。



蛻變或許是每個設計師都會經過的階段,演講中羅子雄和大家分享98年時第一個網頁設計作品:


然而,透過不斷的看‧做‧想,如今打造出非常具質感的多種設計作品:



從「看」 到「看 2.0」


在開始著手設計前,很重要的是要能夠分辨設計的好壞?了解什麼是好的設計?什麼是糟糕的設計。其中,審美觀的培養是最簡單也是最難的,簡單的是它只需要透過你的眼睛去觀看,去觀察;困難的是它需要不斷持續地累積,觀看大量的優秀作品。

透過網際網路,我們可以輕易地接觸全世界優秀的設計作品,像是behance, pinterest, dribbble, fwa, 等等。我們可以透過大量的瀏覽來提升我們的審美觀。


「那麼,我們怎麼樣知道自己審美觀真的提高了呢?」

不知道你有沒有過類似的經驗,在某個時期收集了很多自己認為美的、好看的設計,也常常可能是自己的作品,過了一陣子後,你回過頭看當時的作品時,突然覺得當時設計的東西(或是收集的作品)好像沒那麼好看了,這時候就代表你的審美觀有了進一步的提升。

從單純的「看」到「看 2.0」,最重要的是不只是去看你專業領域內的作品,如果你是前端工程師,那麼你也應該接觸平面設計、動畫設計、工業設計、甚至是室內設計,如此才能讓自己的設計和思維不被侷限在你所處的領域當中。


從「做」 到「做 2.0」


當我們具備基本的審美觀,能夠判斷好作品和糟糕的作品之後,下一步我們要做的就是 ─ 動手。

工欲善其事必先利其器,實際開始動手前,我們要知道如何使用工具。在資訊發達的現在,如果你想學 PhotoShop ,只要上網搜尋就可以找到很多免費的教學文章或影片,在這麼多學習的資源中,羅子雄建議大家,千萬不要看單純教如何操作軟體的影片,因為看完影片之後,你依然不知道如何去創造出一個完整的作品,所以他建議初學者要看的是基礎實例教學。

好的教程應該是要讓你不僅可以學到相關的軟體操作技能,還會學到如何創造一個完整的作品。


那麼,在自學的過程中碰到問題該怎麼辦呢?

除了 Google 之外,我們可以試著把一個看似複雜的問題開始拆解成許多單純的小問題。例如,如果你看到了一個很有質感的網站,想要試著模仿練習,一開始你可能完全不知道該如何切入,這時候,你可以開始切割問題,把大問題拆成小問題。可以將完整的網站切成導覽列、sidebar、banner等等的小部分,在切成小問題之後,我們比較能夠思考它是怎麼做出來的。

如果你仍然覺得無法解決,就繼續試著拆分,一直拆分到你覺得可以通過短時間的學習和練習,把這個問題解決掉。

從「做」到「做 2.0」,我們不只是模仿,而是可以根據自己喜歡的事物進行 redesign ,或者是透過參加競賽來挑戰自己的能力。

將 facebook 進行 redesign



從「想」到「想 2.0」


接著,從觀察到的設計作品中開始思考,這個作品為什麼要這樣設計?為什麼要這樣創作?許多看似理所當然背後其實都有其設計的理由

為什麼要在電話的ICON外加上圓角方框框起來?

為什麼這個圓角矩形的像素要是23px呢?

在從「想」到「想 2.0」的過程中,我們從單純的「模仿」開始思考,從別人的作品得到「借鑑」後進行「改進」。

好的設計師拷貝,偉大的設計師偷
從模仿、借鑑到改進

最後我們也可以根據不同的元素進行「組合」,例如,當講者把彩虹、低像素的圖像和貓進行組合後...


就組合成了「彩紅貓」!?



從入門到專業 ─ 找對方法持續地練習


想要從入門到專業別無他法,我們須要持續不斷的練習,而10000小時的練習將近是5年的時間。

人們眼中的天才之所以卓越非凡,並非天資超人一等,而是付出了持續不斷的努力。一萬小時的淬煉是任何人從平凡變成超凡的必要條件。─瑪律科姆·格拉德威爾。



2016年8月1日

前端工程師必備 — 最實用的 Chrome 套件集合(chrome extension)

前端工程師必備 — 最實用的 Chrome 套件集合

在這一次的分享中,我們整理了12個前端工程師最常使用到的 google chrome 擴充功能,有些是幫我們一覽他人網頁的架構,有些是幫助我們掌握網站元素的細節,有些則是讓我們的作品更容易和他人分享與溝通,快點一起來看看吧!




type sample

透過 Type Sample 可以快速檢視網頁上所使用的字型和字體大小。

下載連結:https://goo.gl/CB89CT

---

範例網站:http://pala.tw/begin-to-learn-python/




FontFace Ninja

FontFace Ninja 和 Type Sample 使用上很相似,除了可以檢視字體、字型之外,亦可以看到文字的行高、字距;但實際測試的時候,微軟正黑體只會顯示”Regular”。

下載連結:https://goo.gl/UNPrcd

---

範例網站:http://pala.tw/begin-to-learn-python/




PageRuler

在製作網頁的過程中,我們常常會需要掌握某些元素的長度、寬度、位置等細節,透過PageRuler這個擴充功能,可以非常簡單的測量元素區塊的大小。

下載連結:https://goo.gl/RGZvB6

---

範例網站:http://www.hexschool.com/




Dimensions

乍看之下會覺得和剛剛介紹的 PageRuler 很接近,雖然它們兩個都能測量長寬,但使用方式截然不同。

PageRuler 是透過選取的方式,選擇自己想要測量的區塊;而 Dimensions 則是會自動去偵測網頁中元素和元素間的距離,但有時候自動偵測到的並不見得你想直接測量的。

下載連結:https://goo.gl/2vlT3n
---

範例網站:https://theinitium.com/




Re:view


在開發 RWD 網頁時,最麻煩的地方往往在於要測試不同瀏覽器、不同裝置間顯示的樣子,透過 Re:view  將可以快速的達到這樣的效果。

Re: view 它會在一個網頁上,同時且同步顯示不同裝置上的檢視樣貌,也就是說,只需要滑動一個頁面,全部的頁面都會跟著滑動。

另外,它分成 BreakPoints View 和 Device Wall 兩種檢視模式。在 BreakPoints View 中,它會檢視該網頁具有哪些 BreakPoints 並依此呈現不同視窗大小;透過 Device Wall 則是可以檢視該網頁在不同手機裝置時會顯示的樣貌。

下載連結:https://goo.gl/FOcWkv

---

範例網站:http://www.hexschool.com/




CSS3 Generator

由於 CSS3 中新增了相當多語法,像是文字陰影、邊框弧度、邊框陰影、漸層、動畫等等效果,有時候還需要透過不同的前綴詞來支援不同的瀏覽器,因此,有一個視覺化的 CSS3 語法產生器是非常實用的。

透過 CSS3 Generator ,除了可以直接以視覺化的介面調整想要的樣式外,還幫你把所需要的前綴詞一併加入。

下載連結:https://goo.gl/XAUl4Q





Stylebot 很像是 chrome 開發者工具中 CSS 的視覺化介面版,和 Chrome 開發者工具一樣,可以透過滑鼠點選頁面上的元素,然後透過介面去調整其中的 CSS 屬性;如果你不習慣使用 GUI 介面,想要直接透過 CSS 語法的方式加以編輯該元素,也可以選擇進階模式來達到這樣的效果。

下載連結:https://goo.gl/lMTX3I
---

範例網站:http://www.hexschool.com/




Wappalyzer

Wappalyzer 當我們看到一個有質感的網站時,身為前端工程師,總是想一探究竟它背後所使用的技術。透過 Wappalyzer 就可以打開背後的黑盒子,從前端看到後端(不就看光光了,我們可以檢視它所使用的伺服器,所使用到的框架、套件,記錄分析網站流量所使用到的工具,一目了然。

下載連結:https://goo.gl/YKjcY
---

範例網站:http://www.thenewslens.com/




Awesome Screenshot

有時候為了和其他設計師溝通,我們必須要把整個網站的頁面擷取下來,你過去是怎麼擷取的呢?是東截一塊、西截一塊,最後再拼拼湊湊的拼回原貌嗎?
透過 Awesome Screenshot ,可以非常容易地把整個網站的頁面給擷取下來。

在 Awesome Screenshot 中,支援整站頁面截圖、可視頁面截圖、選擇區域截圖,另外,它也支援在該頁面上進行註解或簡單的編輯,快來試試看吧。

下載連結:https://goo.gl/GEF1no

---

範例網站:https://www.awesomescreenshot.com/




goo.gl URL Shortener

goo.gl URL Shortener 這個外掛雖然只是做到一般縮網址的效果,但是卻異常的輕巧好用,有時候在完成某些頁面後,想要傳給客戶看,這時候可以使用這個縮網址工具,直接複製貼上就可以了。

另外,它背後其實是透過 google short URL 的縮網址功能,所以它也支援可以檢視有多少流量和裝置是來自此縮網址。

下載連結:https://goo.gl/ygGS6





前端工程師常常會需要和後端介接一些資料,而最常使用的資料格式就是 JSON 。這款 JSONView 顧名思義就是當它讀取到網頁是屬於 JSON 資料時,會自動重新格式為易讀的排版樣式,同時附帶有除錯的功能。

下載連結:https://goo.gl/oVzEUV
---

範例網站:http://www.w3schools.com/website/customers_mysql.php





Web Server for Chrome

某些功能可能會需要本機伺服器的情況下才能正常顯示,透過 Web Server for Chrome 這款應用,我們只需要將寫好的網站以資料夾的方式拉入這款套件當中就可以建立起本機端的伺服器。

另外,如果將 “Accessible on local network” 和 “Also on Internet” 的按鈕打勾,如此,就可以將這個網址分享給同網路連線的使用者一同檢視這個網頁,在開發和溝通上都變得更方便。

下載連結:https://goo.gl/xl5z9P




本次分享中部分擴充功能參考自 “Chrome 網頁除錯功能大解密” ,若想進一步學習和 google chrome 相關開發者除錯工具,或者對於部分的 chrome 擴充功能在操作上有不太清楚的地方,都推薦可參考此免費課程。

免費課程網站:https://goo.gl/lW6ahy






在這次的分享中,是否仍有些你常常使用卻被我們忽略掉的漏網之魚呢?歡迎你透過留言推薦讓我們知道,我們將繼續擴充這個相簿,讓更多開發者在進行開發的過程中享受開發的便利與樂趣。





如果你喜歡我們的內容,歡迎在相簿中 tag 同樣有興趣的好友們,與他們一起分享,一起討論,一起求進步。

若對於當中的 chrome 擴充功能有不清楚操作的地方,也歡迎留言一同討論喔!

2016年7月31日

[心得] 我的第一堂付費簡報課 — 精準設計簡報實戰技巧

精準設計簡報實戰技巧


在台灣,有越來越多「老師」、「教練」或者是工作坊都是以教學簡報製作和講演技巧為核心出發,而且這類的演講或工作坊大多都非常昂貴(用昂貴來形容真的不誇張...),也因為這些課程常常不是我所能夠負擔的,因此大部分在學習的時候我都是透過網路資源自己吸收學習。在看過了這麼多教學文章、教學影片之後,到目前為止我最推薦的絕對是「簡報藝術烘焙坊」的 Bill 彭毅弘。

為什麼我這麼喜歡簡報藝術烘焙坊


我幾乎把他放在網路上的所有教學文章(部落格)和教學影片(10分鐘學簡報系列)都看過了,為什麼我這麼喜歡他推出的課程呢?

很重要的一點是 Bill 的教學不打高空,而且說明的很有架構,操作步驟的講解很清楚。有些簡報老師可能花會很多時間在講「心法」,講的都比較概念性或原則性,聽來聽去你大概知道個概念,可是實際上往往還是不會做或無法應用,但是 Bill 的教學(至少我看到的線上教學)常常是概念和實際的技術都會一起說明,他會說明原則性的內容,讓你可以把這樣的概念延伸到其他投影片加以應用,同時也會實際以簡報軟體操作教學。

以下圖為例,這是 Bill 在課程中常用到的圖。他會告訴我們修改投影片可以從顏色和形狀這兩個角度加以切入,以達到減少雜訊,清楚呈現重點的目的,接著他就會以實際的範例說明如何達到這樣的效果。

圖片來源:簡報藝術烘焙坊

因此,當我在 hahow 中發現 Bill 有開設這堂「精準設計簡報實戰技巧」,而且課程售價只要$700時(預購只要$500),就給它報名下去了,因為實在是比外面的工作坊便宜上超多倍啊!這不只是我第一個付費的簡報課,同時也是我第一堂在 hahow 開課的課程。

課程內容


課程內容維持 Bill 老師一貫的風格 — 簡潔、連貫、清楚。上完整堂課,我最想問老師的是(說不定很多人也想問) — 教學課程到底是怎麼錄的,怎麼可以這麼一氣呵成!因為影片中同時錄了人像、投影片、還有螢幕操作,可是這幾個影片間幾乎讓你看不出來有剪接的感覺,整個轉場的效果會讓你上課的時候覺得是一體成形。也因為如此,我就這樣一集接一集的看下去,一不小心...就看完了(還剩下尚未上線的部分)。

在這堂課程中,不會從最最最基礎的 PowerPoint 開始上起,而是從很多實用的部分直接開始說明,讓你很快就可以套用到自己的簡報製作當中,其中基礎的像是自訂快速存取工具列、投影片的母片設定、格式的複製設定、快速進行對齊和均分。


另外,對於投影片中常用的遮罩效果也做了非常清楚的說明,從最基本的遮罩的製作,一直到用進階「合併圖案」中減去或交集的方式都講解的非常清楚:
課程片段截圖:利用遮罩可以達到的三個目的。

如果你是對於扁平化圖片的簡報設計有興趣,課程中也提供 Noun Project Icon 的使用方式;或者也可以透過簡單的幾何圖形,搭配「合併圖案」中聯集的技巧,達到自製 ICON 的成果:

課程片段截圖:利用合併圖案自製ICON。

另外,連 Office365 中最新的轉化功能,也做了相當清楚的解釋和實際案例示範,看得過程中,真的是讓人對於這個轉化功能的效果感到驚呼連連:

課程片段截圖:透過office365最新轉化功能可達到的目的。

總結


總體來說,我覺得這是一堂CP值相當高的課程,可以學到很多實用的觀念和技巧,同時價格又很划算,非常推薦想要提升簡報設計能力的人學習。

最開心的是,據 Bill 老師所說:「上架只是里程碑而已,未來我們更會持續更新課程」,就讓我們一起期待未來更多老師分享的課程上架吧!


2016年7月22日

[筆記] 親手打造屬於你自己的 JavaScript Framework/Library(中)




在上一篇 [筆記] 親手打造屬於你自己的 JavaScript Framework/Library(上),我們開始建立了自己的 framework ,並且可以成功運用它來建立物件,但在這個物件裡面還沒有方法,因此在這篇筆記中,我們就要繼續往下做嘍。

在 framework 中建立變數


在開始建立方法(method)前,我想要先建立一些變數是我之後可以在方法中使用,但這些變數又不會和外層的 global environment 有所衝突,我們可以在哪裡建立這些變數呢?
我們可以直接將變數建立在這個 IIFEs 裡面就可以了,我們不用放在 prototype 或 function constructors 裡面,這樣會佔據額外的記憶體位置,但是,當我們需要使用時,透過 closure的概念 我們依然可以提取到這些變數。透過這種方法,我們可以讓使用 framework 的人沒有辦法去改這些變數的值,但是在使用 method 的時候,仍然可以提用到這些變數。


因此,就讓我們繼續寫下去啦。

於是我們的程式碼現在長這樣子,多了第8-23行,我分別建立了變數 supportedLangs, greeting, formalGreetings 和 logMessages 這四個變數。


  (function(global, $){
  
    var Greetr = function(firstname, lastname, language){
      return new Greetr.init(firstname, lastname, language);
    }
  
    var supportedLangs = ['en','zh-tw'];
  
    var greetings = {
      en = 'Hello',
      zh-tw = '你好'
    };
  
    var formalGreetings = {
      en = 'Greetings',
      zh-tw = '歡迎您'
    };
  
    var logMessages = {
      en = 'Logged in',
      zh-tw = '登入'
    };
  
    Greetr.prototype = {}
  
    Greetr.init = function(firstname, lastname, language){
  
      var self = this;
      self.firstname = firstname || '';
      self.lastname = lastname || '';
      self.language = language || 'zh-tw';
  
    }
  
    Greetr.init.prototype = Greetr.prototype;
  
    global.Greetr = global.G$ = Greetr;
  
  })(window, jQuery)



建立方法(method)


有兩個地方可以放置我們想要的方法,分別是如下圖的第 1 部分和第 2 部分。但是我們知道,如果放在這個 function constructor 中(第 2 部分)變成每一個所建立的物件都會直接帶有這個方法,如此會佔據相當多的記憶體空間;所以,比較好的方式是利用原型的概念,把根據這個 function constructor 所建立的物件,都可以使用到的方法,放到第 1 部分的 prototype 當中



接著,我們要來在 prototype 中開始建立一些 framework 裡面可以使用的方法。

我們先來看一下建立完方法後完整的程式碼長什麼樣子,這些程式碼都是放在Greetr.prototype{ } 內,接著我們再來分別一一解釋每個方法的意義:


  Greetr.prototype = {
    fullName: function(){
      return this.firstname + ' ' + this.lastname;
    },

    validate: function(){
      if(supportedLangs.indexOf(this.language) === -1 ){
        throw "Invalid language";
      }
    },

    greeting: function(){
      return greetings[this.language] + ' ' + this.firstname + '!';
    },

    formalGreeting: function(){
      return formalGreetings[this.language] + ' ' + this.fullName();
    },

    greet: function(formal){
      var msg;

      //  if undefined or null, it will be coerced to 'false'
      if(formal){
        msg = this.formalGreeting();
      }else{
        msg = this.greeting();
      };

      if(console){
        console.log(msg);
      };

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },
    log: function(){
      if(console){
        console.log(logMessages[this.language] + ' ' + this.fullName());
      }

      return this;
    },

    setLang: function(lang){
      this.language = lang;

      this.validate();

      return this;
    }

  }


fullName(第3行 - 第5行):這個 method 很簡單,只是讓我們可以得到使用者的全名。

validate(第7行 - 第11行):這個 method 主要是用來檢測使用者所輸入的語言我們有沒有支援。記得我們剛剛在最上有建立了一個陣列是 supportedLangs ,裡面含有兩個元素,分別是 en 和 zh_tw,如果使用者在參數當中所輸入的語言並不屬於這兩種時,我們使用 indexOf 這個函式時,就無法在 supportedLangs 的陣列中找到,於是就會回傳 -1 的結果。此時,我們就要拋出錯誤訊息。

greeting(第13行 - 第15行)、formalGreeting(第17行 - 第19行):這兩個 method 都是讓我們跟使用者打招戶用的,其中函式裡面的 greetings 和 formalGreetings 其實是我們在前面所建立的物件,這裡我們透過括號 [ ] 的方式,來取得該物件的屬性值(因為 [ ] 內才可以放變數)。
另一個可以注意到的地方是,在 formalGreeting 這個函式的最後是使用 this.fullName( ),也就是直接呼叫並執行了前面第3行 - 第5行的這個 method。

greet(第21行 - 第38行):這個 method 稍微比較長一些。透過 greet 這個方法,我可以直接控制我要使用的是greeting 或 formalGreeting ,而不用打出這兩個方法。我們利用強制轉換的概念(coercion),如果我們給予的參數 formal 是 true 的話,那麼請幫我們使用 formalGreeting,否則使用 greeting。
為了避免有些IE版本不支援 console 這個物件,我們用 if(console) 這樣的方法,意思是如果有 console 這個物件的話,在幫我輸出。
再來很重要的一個是第37行的 return this ,記得我們前面提到的方法鍊(method chaining)嗎?透過方法鍊我們可以讓一個方法接著一個方法接下去使用。
log(第39行 - 第45行):透過這個 method 我們可以回傳登入的訊息,這同樣有使用到方法鍊的技巧。

setLang(第47行 - 第53行):透過這個 method,我們可以改變我們當初在參數中所建立的語言,同時為了驗證這個語言是不是我們所支援的,我們在這個 method 中加入了 this.validate( )來驗證,validate( )是我們在第7行 - 第11行所寫的 method 。這也同樣有使用到方法鍊的技巧。




寫到這裡,我們就差不多把我們 framework 當中要建立的方法給建立完畢啦,接著就讓我們來看看怎麼使用了!

測試所建立的 framework


接著在 app.js 中,我們可以試著來測試一下所建立的 framework 拉。

假設我輸入:


var g = G$('PJ', 'CHEN'); //  language的預設是zh-tw
g.greet().setLang('en').greet(true);


我就可以得到以下的結果。因為我們有使用了方法鍊的技巧,所以我可以一個接著一個方法的使用,當中我又用了 setLang 這個方法,把預設的語言改成英文:


接著,假設我輸入:

var g = G$('PJ', 'CHEN'); //  language的預設是zh-tw
g.log().setLang('zh_cn').greet(true);

首先,會回傳登入的訊息,接著因為我把語言設成 "zh_cn" 但實際上,我們並不支援這樣的語言,所以回拋出錯誤的訊息給我們。大家可以自己測試自己所寫的 framework 來測試玩玩看。


程式範例


(function(global, $){

  var Greetr = function(firstname, lastname, language){
    return new Greetr.init(firstname, lastname, language);
  }

  var supportedLangs = ['en','zh_tw'];

  var greetings = {
    en: 'Hello',
    zh_tw: '你好'
  };

  var formalGreetings = {
    en: 'Greetings',
    zh_tw: '歡迎您'
  };

  var logMessages = {
    en: 'Logged in',
    zh_tw: '登入'
  };

  Greetr.prototype = {
    fullName: function(){
      return this.firstname + ' ' + this.lastname;
    },

    validate: function(){
      if(supportedLangs.indexOf(this.language) === -1 ){
        throw "Invalid language";
      }
    },

    greeting: function(){
      return greetings[this.language] + ' ' + this.firstname + '!';
    },

    formalGreeting: function(){
      return formalGreetings[this.language] + ' ' + this.fullName();
    },

    greet: function(formal){
      var msg;

      //  if undefined or null, it will be coerced to 'false'
      if(formal){
        msg = this.formalGreeting();
      }else{
        msg = this.greeting();
      };

      if(console){
        console.log(msg);
      };

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },
    log: function(){
      if(console){
        console.log(logMessages[this.language] + ' ' + this.fullName());
      }

      return this;
    },

    setLang: function(lang){
      this.language = lang;

      this.validate();

      return this;
    }

  }

  Greetr.init = function(firstname, lastname, language){

    var self = this;
    self.firstname = firstname || '';
    self.lastname = lastname || '';
    self.language = language || 'zh_tw';

  }

  Greetr.init.prototype = Greetr.prototype;

  global.Greetr = global.G$ = Greetr;

})(window, jQuery)




→回到此系列文章目錄



2016年7月19日

[筆記] 親手打造屬於你自己的 JavaScript Framework/Library(上)




在上一篇[筆記] 跟著JQuery原始碼一起學習程式設計中,我們從 jQuery 的原始碼中,學到了一些建立 framework 的概念和技巧,在這篇筆記中,我們就要來試著打造我們自己的 framework 啦。

Requirement


當我們在打造一個 framework 時,事先規劃好這個 framework 要具備哪些功能是很重要的,而不是一股腦的就打開程式編輯器開始寫,所以我們來規劃一些這個 framework 要具備哪些功能吧!

在這系列的課程,我們大部分都是用 greeting(打招呼) 來當作程式撰寫的例子,在這堂課中也不例外,我們要來打造一個和 greeting 有關的 framework ,我們把它稱做 "greetr"。

我們的需求是這樣的:

  1. 當我們告訴它我們的姓(lastname)名(firstname)還有選擇的語言(language)時,它可以用正式(formal)和非正式(informal)的方式和我們打招呼。
  2. 支援英文(English)和繁體中文(zh_tw)兩種語言。
  3. 是一個可重複使用的 library/framework,也就是說,每一個安裝此 framework 的人可以直接使用,不會和它原本程式碼有所衝突。
  4. 和 jQuery 只需要輸入 "$( )" 一樣,我們可以使用 "G$( )" 來建立物件。
  5. 支援 jQuery ,可以把 greetr 產生的訊息直接顯示於HTML中。

HTML部分


在HTML中,我們總共會匯入三支js檔,第一支是 jQuery,因為在我們的 framework 中會使用到一些 jQuery 的功能;第二支是 greetr.js 這支就是我們寫 framework 的地方;最後一支是 app.js ,這一支則是我們用來應用我們所寫的 framework 的地方。


  <!doctype html>
  <html>
  <head>
      <meta charset="UTF-8">
      <title>Untitled Document</title>
  </head>
  <body>
    <script src="jquery-3.0.0.js"></script>
    <script src="greetr.js"></script>
    <script src="app.js"></script>
  </body>
  </html>



讓程式碼位於安全的位置

現在,我們可以開始在 greetr.js 中開始編輯我們的 library 了。

首先,為了避免我們所寫的 framework 被外層的變數所影響,我們要善用 IIFEs ,也就是將我們所寫的 library 放到 IIFEs 裡面。
同時,因為我們要讓我們的 framework 能夠影響到 global 的內容,同時還要支援 jQuery ,因此在參數的地方,我們會帶入 global 和 $ 。



不需要使用 new 就可以建立物件


在上堂筆記中看過 jQuery 的架構後,我們希望可以將學到的東西應用到我們的 framework 中,也就是說,我們希望可以輸入 var g = G$(firstname, lastname, language) 如此就能建立出一個新的物件而不用用到 new 這個關鍵字,我們可以怎麼做呢?
從上一堂課的筆記中,我們知道我們可以利用 return 一個 function constructor 的方式來達到這樣的效果,如圖中第 1 部分所示,而圖中的第 2 部分,才是我們真正建立函式的地方。

第 1 部分之所以可以寫在第 2 部分前面,是因為第 1 部份要在我們執行 Greetr 時,才會真的加以執行


建立函式裡面的預設值


接著,我們要在函式裡面為 firstname, lastname和 language 來建立預設值,還記得我們可以怎麼做嗎?

首先,因為我們這是一個 function constructor ,為了避免 this 可能在後面使用時碰到一些問題,所以在第 1 部分的地方,我們用 var self = this 這樣的方法,來避免 this 在後續操作上可能會碰到的問題;在第 2 部分的地方,我們則是透過 JavaScript 中強制轉換(coercion)的特性,使用 OR operator  || 的方式來達到預設值的效果。


建立建構子的原型(prototype)


接著,我們要來建立這個函式建構子(function construct)的原型了,還記得原型(prototype)嗎?
為了程式容易閱讀,我想要將這個建構子的原型,用 Greetr.prototype 來表示,於是就如下圖第 1 部分所示。

可是,我們知道,我們現在函式建構子的名稱是 Greetr.init ,所以這個建構子的原型名稱其實是 Greetr.init.prototype 才對,因此,如同 jQuery 中所使用的方式,我們可以在第 2 部分的地方寫上 Greetr.inti.prototype = Greetr.prototype ,如此,我們就可以直接在 Greetr.prototype 中撰寫程式碼了。


使用 G$( ) 即可建立物件


就像在 jQuery 中,我們可以使用 jQuery( ) 或 $( ) 來建立物件,在這裡,我們希望我們可以使用 Greetr( ) 或 G$( ) 這兩種方式都可以建立物件,記得在 jQuery 中是怎麼做到的嗎?

我們只需要加上這一行,就可以達到這樣的效果了。


到目前為止...


到目前為止,我們的框架 greetr.js 長這樣子,而且它已經可以簡單使用了。


  (function(global, $){
  
    var Greetr = function(firstname, lastname, language){
      return new Greetr.init(firstname, lastname, language);
    }
  
    Greetr.prototype = {}
  
    Greetr.init = function(firstname, lastname, language){
  
      var self = this;
      self.firstname = firstname || '';
      self.lastname = lastname || '';
      self.language = language || 'zh_tw';
  
    }
  
    Greetr.init.prototype = Greetr.prototype;
  
    global.Greetr = global.G$ = Greetr;
  
  })(window, jQuery)


如果我們在 app.js 當中,輸入以下內容:


var g = G$('PJ', 'CHEN');
console.log(g);


已經可以成功獲得一個物件了!






→回到此系列文章目錄



2016年7月16日

[筆記] 跟著JQuery原始碼一起學習程式設計


在這篇筆記中,我們會要進入jQuery的原始碼中。

雖然這堂課老師一直希望我們可以自己多透過這些開源的程式碼來學習程式設計,但是說真的,直接進入原始碼的時候,還真的是看不太懂阿...,好險有老師一步一步點出重點來,就讓我們從中發掘一些可以學習或應用的部分,趕快看下去吧!

看一下JQuery這個物件


我們可以先輸入以下的HTML架構:

  <nav>
    <ul class = 'people'>
      <li><a href="#">Home</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Products</a></li>
      <li><a href="#">Contact</a></li>
    </ul>
  </nav>


然後我們可以在js檔中,輸入以下jQuery語法:

  var q = $("ul.people li");
  console.log(q);


如此,我們就可以檢視看看jQuery物件的樣子。在console視窗中我們可以看到它是一個array-like的東西,名稱是 jQuery.fn.init ,先稍微對這個名稱有個印象,等等我們在去原始碼中看看這個東西:


另外,我們可以在 __proto__ 裡面,看到一大堆的jQuery方法:


接著,就讓我們正式進入jQuery原始碼啦!

說實話,要我自己看這個原始碼,還真的不知道要從何看起...,不過透過老師的引導,幾乎是把這堂課所學到的重要內容再次複習了一次。

IIFEs的使用


一打開jQuery原始碼,我們就會看到一個IIFEs,記得我們有提過在IIFEs中所建立的變數,都不會影響到Global Execution Context所建立的變數,也就是說,透過IIFEs,它避免了我們的變數間可能會互相干擾覆蓋的情況 ,而jQuery同樣是使用了IIFEs的方式來撰寫這個library。

IIFEs開頭
接著,我們可以往下看到IIFEs的結尾,其中有一串是 typeof window !== "undefiend" ? window : this ,這個意思是如果window的類型不是undefiend的話,則global object就是window,否則是this(根據當時的環境所決定)。

IIFEs結尾


為什麼在jQuery中不需要輸入 new 來建立物件


我們知道在jQuery中,我們只需要寫 $( )jQuery( ) 就可以建立jQuery的物件了,而不需要使用關鍵字 new ,為什麼可以這樣呢?

原因在於它用了 return new ... 這樣的用法,等於當我輸入jQuery時,它就會直接幫我帶入 new 這個關鍵字,讓我們不用每次用new這個關鍵字。

從這裡,我們也可以知道 jQuery.fn.init 是一個function constructor,我們應該可以在後面的地方找到它。


在jQuery中為什麼可以使用方法鍊(method chaining)?


在jQuery中,我們可以在一個方法之後直接接著另一個方法使用,像是這樣:

  var q = $("nav ul.people").addClass("newClass").removeClass("people");
  console.log(q);


我們可以直接針對ul這個元素,去添加class,同時直接在後面移除class,而不用重複打兩次。這種一個方法接著一個方法,而且每個方法都可以影響到父層物件的情形,我們就稱為方法鍊(method chaining)

這是一個很有趣的情形,因為我們知道第一個 addClass 因為接在 jQuery 物件之後,所以它會是 jQuery 物件裡面的一個方法,同時可以對 jQuery 物件產生影響;但是,對於 removeClass 這個方法來說,它是放在另一個方法後面,為什麼可以找到這個方法,而且仍可對我們的 jQuery 物件有作用呢?

為了要解答這樣的問題,我們要進入到 jQuery 原始碼中一探究竟。

搜尋 addClass 這個方法的地方,我們會發現到,這個方法的最後有一個關鍵,也就是它 return this ,而這裡的 this 指的也就是我們的 jQuery 物件,因此透過這個 return this,它可以先針對物件進行欲要進行的方法後,最後再次將它回傳成一個物件,於是,它就可以繼續在接著下一個方法,形成一個方法鍊的作法。

也就是說,addClass 這個方法執行完後,會回傳原本的 jQuery 物件,因此變得就像這樣子:

  var q = $("nav ul.people").removeClass("people")



在原始碼中,我們可以看到,在 removeClass 或其他許多的方法中,最後都會加上 return this 。


為什麼在jQuery中可以使用 $ 或者 jQuery 來建立物件?


接下來我們想要來看,為什麼在jQuery中,我可以透過 $( ) 或者 jQuery( )這兩種方式來建立物件都不會有問題呢?

我們在程式最下方的地方可以看到這個,而這也就說明了,為什麼我們可以使用 jQuery 或 $ 的方式來建立物件,因為他最後都是指稱到 IIFEs 裡面的這個 jQuery 函式。





→回到此系列文章目錄