Taipei GUTG – Android 行動廣告/行動付費討論

無意中看到Taipei GUTG的活動,剛好有興趣就報名參加了。

Alex from Vpon

Vpon廣告平台介紹

第一段是Vpon的Alex來推銷Vpon這個廣告平台。很有趣,分享了很多數據,據稱是第一次公開的數據。有兩百隻App在使用Vpon這個廣告平台,其中iPhone與Android的比例是2:3。Alex是說符合台灣的SmartPhone比例?聽起來有點怪怪的,全球iPhone與Android比例不是2.5億比上2億嗎?

還有一個有趣的information,買Android Phone的人有很大的比例是不上網的。恩,我想這會不會代表Vpon應該多花一點心力去拉iOS的開發者,是吧。

另外,週末時SmartPhone的流量比平日高,聽起來算合理,週末一堆人在facebook上打卡就看得出來。然後iOS的流量禮拜天到禮拜一的凌晨高於Android相當大的一個比例。Alex是說因為大家在看偶像劇Video?這點個人就覺得有點不合理拉 XD 不過她說他前一晚才被指派來的,所以資料準備不充分應該也是正常。

至於台灣各地區的智慧型手機比例,Vpon表示天龍國iOS用戶佔全國52%,即使是新北市也是極低比例。大部份集中在北部應該也是沒錯,台灣大部份年輕人都集中在北部工作。中南部的鄉親可能就比較少會拿手機上網?我是這麼想的。不過我還是覺得台北市跟新北市的比例(甚至是桃園)的部分會有點問題。因為通勤的關係,這部分資料可能會失真。

至於錢的部份,Vpon宣稱,CPM通常都是AdMob的2到10倍,這倒是滿驚人的啊。而且有很多大咖的廣告主。今年六月還拿到摯信資本首輪七百萬美金的投資。真是太驚人了。而且Vpon是從2007年就成立,這麼早就看準這一塊市場實在是很了不起。

淺談 Android 應用的破解與保護之道 – Sam Lu

Android Developer大部份應該都會知道Sam Lu,不然至少也會看過他的ysl的程式天堂。不然的話就是你應該會安裝過他的大作App 2 SD。Sam一開始有宣傳一下關於無國界工程師協會在募集你家沒在用的筆電,要分送到全世界各地比較弱勢的小朋友。還有最近一個Open Source Project(aTrufileHog),主要是在收集Android device的info,比如說你現在這隻手機是HTC的Sensation,然後大部份比例的人都在用ICS了,你卻還在用ginger bread,那就代表 … 。總之,對GAE有興趣的話可以跟Sam聯絡。

至於如何避免你的App被破解?Sam提出一個有趣的見解:不要讓你的App進排行榜XD 這大概是一個擁有四百多萬用戶的App的開發者才能夠這麼說的啊。畢竟能進排行榜的還是少數啊。

Sam提到,要避免破解之前,你必須要先會破解,他有提到他大學時期破解Pac Mac以及Dig Dug的經驗。然後回到Andorid上,他說License Verification Library以及 in-app billing對於App的保護都沒什麼用。至於一般破解的步驟都大概就是從你檢查驗證並跳出的部分去找特徵,比如如果你跳一個Message,那就是可以reverse code去找出這個字串的地方,就可以很簡單地破掉。或是一些 if (XXX is true) then finish 之類的,也很容易被找出來。所以大概幾個原則是:

  1. 按照Google  IO的建議檢查是不是flag是否為debug,是的話做一些防護措施。簡單不過大致上不太有用的保護。
  2. 驗證Signature特徵,也算是一層基本保護。
  3. 偵測到破解的時候,不要立刻反應,延遲一下或是丟到 handler去讓message dispatch去作對應的動作,可以增加破解的難度。
  4. 要多灑一些地方去做驗證,並且要反軟體工程,避免模組化這類驗證的source code,不然很容易就會被猜出來。
Sam還提到關於Ad Blocker的故事,有天他心血來潮更新了一個版本,把有Ad Blocker的用戶擋掉,於是那個禮拜他收到了好多一顆星,然後各種語言的謾罵都在Android Market上出現了。Sam表示,壞人的聲音總是比較大,跟社會一樣。至於偵測Ad Blocker的方法,如果有用linux的人大概會知道/etc/hosts這個檔案,有root過的device就可以去改這個檔案,如果這裡面有 admob 之類的網址大概就是有AdBlocker。

藏密JNI – 把機密的程式碼藏於 JNI 中防盜版 – 林昱宏

第三段,Alger來分享關於如何把驗證的部分寫在JNI中,大致上原則是與Sam相同,不過Alger表示JNI是地獄,如果沒有必要,不要輕易下到地獄去。

PhoneGap on iPhone

如果有寫過Android上的WebView-based APP,應該都會對於WebView.addJavascriptInterface API非常熟悉。這個API可以直接把Android native Java object的interface直接export到WebView,這麼一來JavaScript就可以直接呼叫Native API,這一條路就可以讓你直接用HTML刻UI,然後快速的開發APP。

雖然已經有一些library把這些細節包裝起來,比如PhoneGap。不過我通常還是會自己來,避免因為太多的wrapper導致效能的問題。通常WebView-based APP的效能到開發後期都會是一個很大的問題。所以除非是個很簡單並且沒有牽扯到大量資料的APP,不然最後還是走native app。這是我個人的一點經驗,不然最後會因為tune效能而很想打掉重練。

最近想要在iOS上做一個小型APP,於是想說先用HTML做一個快速版。不過立刻遇到挫折,iOS上的UIWebView沒有這個addJavascriptInterface API啊。不過既然PhoneGap都作了,所以我就拿來研究一下,然後結果就是兩段式的作法:

第一步,先把要傳遞的command / arguments記在一個queue裡面。然後在把WebView的URL設為 gap://ready,並且在Native Objective-C裡面去處理,如果遇到"gap://ready"就會用stringByEvaluatingJavaScriptFromString去跟WebView要JS的資料。恩,是把路打通了。但不知怎麼就覺得這方法實在滿蠢的啊。這樣的話就更慢了。難怪我之前用PhoneGap去叫Camera View出來的時候那麼慢。

BTW, 最新版的PhoneGap(1.2.0)的Plugin功能應該是爛掉了吧?

[Android App] Lady Plurk 上架了

http://dkstyle.co.cc/Product/LadyPlurk/

在十天的開發時間之後,總算完成 Lady Plurk 這個 App 了。雖然開發的時間非常快速,但98%都是Native,而不是HTML5。

主要特點

這是一個以粉色系為基調設計的Android Plurk App,專門設計給女生用的Plurk Client。 整個UI的視覺呈現參考iPhone視覺設計,是一個你一定要擁有的Plurk App。

功能

  1. Plruk API 2.0
    完全已最新的Plurk API 2.0為標準而開發,完全透過OAuth作使用者認證,Lady Plurk完全不經手帳號密碼資訊。絕對保證您的資料隱私安全性。
  2. 姓名顏色
    支援Plurk新功能,會根據噗友設定的姓名顏色呈現。
  3. 基本功能
    發噗,回噗,私噗,表情符號,說讚,消音,刪除一定俱全。
  4. 進階功能
    上傳照片,拍照,或是透過媒體瀏覽器。也可以觀看使用者分享的照片。
  5. 開發過程
    Lady Plurk僅有一人開發,十天內完成。可能會有一些Bug,煩請將您遇到的問題留在下面。感謝。

Idea – 婚禮現場即時照片串流

Photo from http://santabarbaravenues.com/photos/

百年結婚潮,你被炸了幾次?婚禮帶來的商機好像都在今年浮現了?
上個月打球的時候,朋友問我說有沒有辦法有一個網頁可以讓參加他婚禮的人可以把照片拉上去就直接上傳的?我說當然可以啊,HTML5可以滿足你的需求。而且已經有很多線上網站都已經有這個功能了,比如Gmail的附件,或是Google Docs的上傳。朋友的目的單純只是想要收集婚禮當天大家拍的照片,不過這個問題在我腦中一直繞著,我總覺得這件事情應該可以更有趣,可以在更加延伸一點。

婚禮或活動正式開始前,你都在做什麼?

大家一定都有經驗,參加婚禮的時候不想太早到,覺得會枯等很久,也不想太晚到,可能會被安排到長官桌,長輩桌,或是一堆你不認識的人的桌。社交能力好的人,在等待的時候會四處串場,不好的人,你一定看過,反正就坐在位置上不是玩手機,不然就是無意識地不斷的喝桌上的那杯飲料,然後看著不斷重覆的婚紗照投影片。同時,你一定也會看到有一堆人在拿手機拍照,準備分享到facebook。所以看來等待這件事情應該可以更有趣的 …

增加參與感,會很有趣

在等待,或是婚禮進行當中,各個角落都在發生許多事情,而這些角落肯定都會有幾隻手機正在拍照。所以每個角落在同一時間其實都被記錄著,那如果這些照片是在拍照的當下就被收集下來,並且同時實況的照片串流就被投影在螢幕上勒?我覺得很有趣是因為:

  1. 坐在位置上假裝在看重覆了一百遍的婚紗照slideshow的人可以有一些真實的content可以看
  2. 可以讓大家有拍照的動機,因為分享馬上就可以被看見(我是這麼想的,可以分享是一個非常大的拍照動機)
  3. 大家可以參與更多平行時空中的事件(比如在新娘休息室發生的事件,在禮金桌發生的事情 … 等等)
  4. 並且,原本我朋友想要收集到的照片一定會多更多,在當下活動發生當時願意上傳的人一定比你參加完婚禮回家後還願意把照片放到電腦裡並且開啓你的網頁來分享的人多。(缺點就是有些在現在用單眼拍照的人無法及時分享,也許EyeFi可以解決這問題。)
  5. 甚至可以讓沒空來參加婚禮的人可以看一下婚禮的即時狀況

技術難度?

這整件事情要完成其實好像沒有什麼技術難度,你首先必須有一個可以拍照後立刻可以上傳到特定地方的網址的App,以及一個會自動更新照片串流的網頁。然後,現場必須有夠力的WiFi AP(可以加速上傳的latency),一個夠大的空間存放照片,一部筆電,一臺投影機,搞定收工。

商機

這整件事情看起來就可以當成一個服務來賣,不只可以應用在婚禮,任何的活動都可以用這個模式來做。開家公司做這件事情吧(笑)

Android ListView Fast and Smooth Scroll 終極秘技

最近在做一個案子,內容大概就是模擬Facebook的塗鴉牆。因此免不了就需要跟Facebook比較一下ListView更新的效能。不過很不幸地 …

不管我怎麼improve,效能就是比Facebook App差!

為什麼?到底為什麼?
於是我找了很多資料,也試了很多方法,最後發現原來Facebook的絕招是WebView!

Android ListView的運作方式

先分享一下基本的ListView運作方式。一般正常的ListView的使用方式通常是一個ListView搭配一個客製化的 ArrayAdapter。資料的部份存放在ArrayAdapter中,所以如果你需要動態的載入內容,就必須在ArrayAdapter 裡面動手腳。

ListView每次在更新UI的時候會根據目前可視範圍去跟ArrayAdapter要一個 View物件(透過ArrayAdapter.GetView()),所以在沒有任何最佳化的情況下,你每次都必須在GetView裡面去Inflate出一個View,並且把你需要的資料填進去,然後回傳給ListView當做一個Row來呈現。

看起來好像沒什麼問題,ListView每次都只會Keep目前”看的到”的View,所以不會浪費記憶體。並且每一次呼叫GetView的時候都會把目前的Row的View傳進去做Reuse。一切都很美好,但事實上不是這樣。

如果你的View的內容都不會改變,那這個設計就完美。但實際上就是你一定會需要更動每個Row的內容。於是每一次GetView,就算可以Reuse對應的Row View,但findViewById去拿到這個Row View裡面的Sub-View去把資料填進去無法避免!如果只有一兩個findViewById可能也還好,一多的話,在ListView上面做swipe up/down的動作,你就很明顯的發現scroll好像卡卡的。

Android API Demo Sample裡增進效率的方法 – ViewHolder

我先找了一下Android的Sample裡面是怎麼implement ArrayAdapter,於是發現裡面就有個EfficientAdapter範例。裡面有用了一個ViewHolder Pattern。主要的Improve是只做一次findViewById。方法是這樣,在第一次inflate View的時候,就用一個 ViewHolder instance去把你findViewById的View reference都keep下來,然後把這個ViewHolder instance塞到 Row View的 tag (透過  View.setTag ),這麼一來,下次只要拿到可以Reuse的Row View,就只要把這個ViewHolder instance拿出來用就可以了。 不過有個限制,你必須確定你每個Row都會用到一模一樣的View。在稍微複雜一點的狀況下,可能會有一個group header的view,所以沒辦法保證每次拿到的Row View都可以Reuse。再來就是如果你的View裡面有用到ViewStub,那也會有問題,因為ViewStub被inflate之後就整個被取代了,所以keep下來也沒有用。

尤其,我想做的Facebook Wall其中有好幾種不同的View,所以免不了常常需要重新inflate,而且大部分的時候沒辦法Reuse。

還有一點很討厭的是,如果你的GetView裡面需要做很多資料處理的話,如果沒有小心處理而導致引發GC (Garbage Collection)的話,那快速Scroll的時候就更頓了。Android SDK網站有一篇文章專門在講這個topic
這GC其實還滿討厭的,這大概就是不用自己做memory management的代價吧。

網路上大部分在討論可以快速的smooth做ListView scroll的幾乎都是在講ViewHolder:
http://stackoverflow.com/questions/1320478/how-to-load-the-listview-smoothly-in-android
http://yekmer.posterous.com/fast-listview-scrolling-in-android

極端的方法通常都不酷

我看到有一個範例在API demo裡面,並且它絕對可以做到極快速且順暢的Scroll:就是什麼都不做!在Scrolling狀態下,永遠只回傳同一個View,而裡面顯示”載入中”。這樣不會有GC也不不需要做多餘的運算。超快!但問題是:這樣不酷啊(Mark Zuckerberg不是已經告訴你不酷就沒人用?)。所以這個方法也不行。

Facebook 到底怎麼做

就在改了半天還是比不過Facebook之後,我就去decompile Facebook APK,看完裡面的Layout XML之後,媽的!這跟我做的不是差不多,而且它每個getView裡面還要做ViewStub的 inflate耶。於是我開始覺得自己其實不會寫程式 … 所以我就跑去忠孝東路上北科大對面的伯朗咖啡(這裡有快速又免費的WiFi,超棒。唯一的問題是會在你的http packet裡面偷偷放廣告進去),就在這該死的伯朗咖啡偷塞的廣告裡面,我發現,靠!原來Facebook App跟本就不是ListView啊!因為我發現伯朗咖啡的廣告也被塞到Facebook App裡面了!那到底Facebooke幹嘛放那些layout佔空間啊?

多玩了一下,發現登出Facebook App之後再重新登入的第一次跟你之後再點進News Feed的UI長的可是不太一樣。不知道有沒有人發現。左邊的是Web模式,右邊是Native。而且Web模式是真的去載入Facebook上的Web,而不是local的html。

其實我還蠻驚訝的!因為之前曾經做Android平板,完全用Web App的模式做,一直覺得純Web App的UI效能還能做的好啊。尤其在大量的資料下用 JS去做Animation實在是吃力不討好。這時候才發現其實就利用WebView本身的Scroll功能就可以了啊。Facebook完全就是利用WebView本身的Scroll功能,他們只需要把Web的內容填進去就好了。

下次如果有需要對大量內容做Scroll的話可以考慮一下WebView,尤其WebView可以把background設定成透明(Pass zero to make the view transparent.),這樣的話可以很容易的WebView以及Native UI混在一起。效能可能還比調整ListView半天來的好許多。

你其實並不懂JavaScript

偶然在網路上看到這篇文章,覺得很有趣,必須說這篇文章有幾點我不太了解的地方,所以有稍微查了一下,想說乾脆用我自己理解的方式把它翻譯一遍。如果你也恰巧很了解JavaScript,然後發現我理解錯誤,那請一定要糾正我:)

你其實並不懂JavaScript

本文翻譯自:http://www.w2lessons.com/2011/04/you-dont-know-javascript.html
This article is translated from: http://www.w2lessons.com/2011/04/you-dont-know-javascript.html

過去一年多來,我發現到一個正在發生的問題,一個令人不是那麼開心的現象。我一再的看到程式設計師在履歷裡面填入他們實際上不是這麼熟悉而只是稍微接觸過的技術。而在那麼多的程式語言中,最常被用來填充在履歷上的正是JavaScript。

你甚至沒有意識到你不了解JavaScript

之所以會這樣,大概是因為幾乎每一個Web工程師或多或少都會有需要用到JavaScript的時候。在沒有完全了解的狀況下,最常看到的學習JavaScript的方法是依照需求從網路上隨便找一段JavaScript程式碼片段,配合一些間單的編輯之後再進行「剪下與貼上」。這麼一來,工程師從未真正的學習語言可是卻往往會有一種錯覺,自以為自己懂。在經過了一些學習的課程以及JavaScript工作了幾年下來之後,我發現在你真正的了解它之前,你並沒有意識到你其實並不懂JavaScript。看起來這是個惡性循環的問題。而其實你需要的只是有個人來告訴你你並不懂JS,並且你需要一些真正的學習來了解JS。時常面試到一些很驕傲的把JS列在履歷上的人,但其實僅僅只是做過一些從網路上來的程式碼片段擷取而成的onClick的處理函式或是表單驗證的簡單工作。如果有用過或是有一些JS框架(例如JQuery或Dojo)的知識的話很好,但是在沒有完全的了解實作這些框架的JS的情況下,其實並無法真正的熟悉了解這些JS框架。為了展現JS的各種組成原理,我把這些概念區分為基本,中等,進階三個等級:

基礎等級包含:

  • 知道基本的JS語法,例如迴圈,if判斷式,try/catch等。
  • 了解各式函式定義的方法以及指派的方法,包含了解匿名函式的使用。
  • 了解基本的scope原則,包含全域scope (window)以及物件scope (不包含closures)
  • 了解執行context scope以及this的使用
  • 了解各種生成物件以及宣告物件的方法,包含函式本身也是物件的概念。
  • 了解JavaScript的比較運算子的意義,例如 ‘>’,‘ 物件的函式與屬性的索引,並了解這與實際的陣列有什麼不同

中階等級包含:

  • 了解timer的使用,以及使用的時機以及使用的方式,比如用來當做非同步執行的方法。
  • 深度了解callback以及function的使用,例如透過call以及apply兩個function函式來改變context以及function參數傳遞。
  • 了解JSON表示法以及eval函式。
  • 了解closures,包含對於效率的影響,以及如何透過(function(){}())達成private變數
  • 了解AJAX以及物件字串化

進階等級包含:

  • 了解 ‘arguments’以及如何利用 ‘arguments’以及 ‘arguments.length’達成function overloading。以及利用arguments.callee產生遞迴呼叫。特別要注意的是arguments.callee可能會有點問題因為ECMAScript 5 Strict Mode並沒有支援callee。不過jQuery以及Dojo都有用到就是了。
  • 進階的closueres應用,例如self-memorizing functionscurrying,以及partially applied functions
  • Function與html prototyping(prototype chain),並且知道如何reuse既有的JS Objects與函式 (例如array)
  • 物件型別,以及instanceof與typeof的使用
  • Regular expression
  • with敘述式,以及為什麼不應該用with
  • 最難的在於把所有這些串接在一起組成乾淨的強大的快速的好maintain並且跨瀏覽器的程式碼

最後這一點尤其重要,並且也最難做到。在JS允許非常自由寬鬆的寫法的狀況下,你的code很容易就會變成一團混亂的無法maintain的”spaghetti code”(譯註:大概是說你的code邏輯扭扭曲曲而且互相糾纏的意思)。一旦你真正的去學習JS,並且能夠結構化它並且把他們組合成一大型的Web Application,那你就真的精通JS了。而這需要數年的練習,以及可能需要從”錯”中學習,並無法僅僅從書上學到。我自己本身每天使用JavaScript好幾個小時已經好幾年了,並且持續的找到一些更好的方法來結構化我的code。貿然的直接去看一個JS framework是很危險的,因為jQuery code本身會養成寫出無法maintain的code的習慣(譯者:why?)。Dojo提供了些支援透過他的Class與Package系統(譯者:?)。

在Node.js之後,JavaScript已經從前端滲透到後端了,所以我決定把web相關的知識從以上的需求區隔開來。就是在Web(也就是DOM與IE)這一塊給了JS一個不好的名聲,並且嚇到所有的programmer。儘管如此。如果你正在學習Web方面的JavaScript,身為一個好的開發者,有一些其他的事情你必須知道:

  • 有效率的新增,移除,改變DOM nodes。包含透過一些工具(例如,document fragments)來盡可能減少瀏覽器的re-flows。
  • 以跨瀏覽器的方式去存取DOM元件的資訊。最後是透過一些既有的jQuery/Dojo框架。很重要的是必須了解來自CSS或是style tag或是運算過後的屬性之中的差異性。
  • 跨瀏覽器的事件處理,繫結,反繫結,事件傳遞(bublbling),以及如何達到想要的callback context。一樣,這些最後是透過既有的framework來做,但是開發者還是必須瞭解背後的原理。
  • 擴充物件屬性與屬性設定的差異,包含效能的差異以及既有同名屬性的差異。
  • 用來抽取DOM node的regular expression
  • 有效的瀏覽器功能偵測以及適當的防錯機制

從以上的各點看來,JavaScript除了alert(myVal)以及myBtn.onclick=…之外還有非常多東西。在你的copy/paste的來源之外還有許多跟JavaScript相關的東西可以學習,而你必須透過閱讀以及練習來成為一個真正的JavaScript programmer。有兩本很棒的書包含了以上所提到的你必須知道的JavaScript,一本是Douglas Crockford大師的著作:The Good Parts,另一本是jQuery作者 John Resign的著作:Secrets of the JavaScript Ninja。必須說由於每個人都有一個瀏覽器,JavaScript大概是一個最容易可以取得(使用)的一個語言。建立一個簡單的HTML頁面,並且開始測試以上所提到的概念吧!至於履歷上的列表,我會說如果你已經瞭解基本等級的知識並且正往中階等級學習,那你就有資格把JavaScript列在你的履歷表上。一旦你開始開發你自己的JavaScrpt函式而不是只是copy/paste,那麼你大概可以宣稱你懂JavaScript。不過在那之前,請停止說你懂JavaScript。

如果我有漏掉任何一個關於JavaScript的面向,請回覆讓我知道。並且請分享你的經驗關於任何你遇過宣稱懂得JS或是任何語言的人。

請注意到我並不是一個前端開發者,我其實是一個後端的開發者,並且也是full-stack開發者。現今,每一個後端開發者都需要學習JavaScript,而這也正是我這篇文章想要鼓吹的事情。我並沒有想要顯示我似乎很厲害,因為我很難說我知道關於JS的任何一件事情。我想要的只是更多的人可以了解JavaScript是一個巨大並且強大的語言。

(Image source: http://ejohn.org/blog/javascript-performance-stack/)

開發人員工具的重要性

在軟體開發的環境裡面打混了好幾年,所有跟產品時程有關係的一定都是ASAP、Urgent …。但是如果是開發人員工具通常老闆們都不太在意。

卑微的E:需要授權的debugging tool?
偉大的B:不能找破解嗎?沒有這個預算噢。沒有免費的可以用嗎?

卑微的E:我正在看一個tool的bug?
偉大的B:沒修好也可以用吧?還是做些有生產力的東西吧。

以上純屬虛構

大公司也會這樣嗎?

以前一直以為一定是因為公司規模不夠大,所以不重視這個。直到前一陣子發現了一個Chrome Developer Tool的問題,並且回報了之後,很快就收到一封回覆表示這個問題是duplicated issue,於是才發現這問題在2010八月的Chrome v7的時候就已經有人回報過了。這問題是這樣,如果把Web Inspector開著,然後開一個新分頁再切回來,之後Web Inspector就會自己慢慢的變大。那如果你是個Web Developer,這個動作會是非常頻繁的發生。可能遇到一個問題,就開一個分頁去Google一下,或者是累了就開個facebook。這問題對於一般人當然是沒什麼影響,但是令人驚訝的是,這對於Chrome Developer來說應該也會是個很困擾的問題吧?而且對於這麼頻繁的每六個禮拜就有一個release的Chrome,居然過了一年這個問題才修復?

好,所以合理的假設是大部分人都是用FireFox的FireBug,或者是Safari。

生產力會增加嗎?

就一般正常經驗來說,有一個好的開發工具一定會增加生產力。比如果以前剛開始工作的時候用VC寫程式,最後常常會遇到的問題就是memory leak,如果手動去找當然還是找的到,只是運氣不好的話可能會搞很久。當時有個Bounds Checker直接就可以指出大部份的memory leak的問題。之後就可以有更多的時間去做更重要的事情。或者是有時候你會常常需要做一些重複性的工作,比如說除完一隻蟲之後,需要寫log去commit code,然後用一樣的log去填寫bug tracker,然後再去做一些通知的動作。每個軟體工程師大概每天都在做這件事情。但其實大部份的SCM都提供hook scrpt的功能,所以其實寫個script就可以節省下大部份的時間。但在我的觀察裡,不知道為什麼大部分都還是選擇手動。

開發者工具的Console

再來是如果有了開發工具,最好是弄清楚它有什麼能力,可以做什麼。那也可以節省你不少的時間並提高生產力。比如說我之前剛開始用Chrome Debug JavaScript的時候以為Console只能用來看Console.log的debug message,所以有時候我會發很多時間在改code加入console.log然後再重新載入看結果。後來發現自己很蠢,console的功能很強大,其實可以直接執行code。所以在某個斷點下,在console直接下console.log(variable_name)就可以了,根本不需要回去改code。也因為console這麼強大,所以大部份的實驗都可以直接在這上面測試,在這上面的scope跟載入的JavaScript一樣,所以你可以呼叫任何載入的function。

直接修改DOM元件屬性

Chrome開發者工具還有很多可以增進生產力的功能,Google有一個英文版的介紹,另外一個我覺得很棒的功能是可以在inspect mode直接在網頁上的元件點一下,就可以直接定位到這個元件的屬性,並且你可以立刻修改任何這個元件的屬性,包含CSS,而且網頁會立刻反應。很棒的工具,會用的話對生產力有正面的力量:)

image from http://idontgetpolitics.co.uk/post/1380505356/image-by-photojonny-on-flickr-shared-via-a-cc

天下雜誌出版好棒

昨天晚上做了我這輩子從來沒做過的一件事情:發信告知出版社有關於他們所出版書籍的錯別字。本來想說這類的信應該不會有什麼回應的,想不到天下雜誌居然在一天內就回信了。

由於我是第一次這麼有心的做這件事情,所以我也不知道其他出版社是不是都這麼快速的回覆這類的信。但其是我內心以為會跟Knuth一樣,找到一個錯誤就會得到0x$1.00的感謝報酬。哈哈,顯然的是我想太多了。

關於這個錯別字回報的資訊:

facebook臉書效應一書 2011/03/30 第一版第一次印行。
錯字位於第200頁的倒數第二段的第一句:隱私"全"爭議。

發現這個錯誤是上上禮拜在香港機場看facebook effect的時候發現的。當時特地把那一頁折起來,還用筆在上面圈下並註記錯字。回台灣之後就忘記這件事情,昨晚因為看到CNN關於Facebook Timeline的文章,突然想分享很有呼應的一句話:

臉書公司的防禦心態越來越重,他們也不期望用戶會喜歡新設計。在臉書推出新設計後,立即有員工在臉書上成立一個暗諷用戶的群組「我自然而然討厭臉書新首頁」,其群組性質中說到:「我討厭改變,以及伴隨著改變而來的一切東西,我希望我這輩子的所有東西都保持靜態。」 … 另一個人(指臉書員工)諷刺道:「我討厭這個改版,直到再推出另一個新設計,屆時,我將喜愛前面這個改版,激烈反對新設計。」

p.p. 293-4

(http://diggbooks.net/?nid=404)

才不小心翻到那一頁,又剛好坐在電腦前,於是就順手的寫了信。接著就是看看新版是不是真的會記得做修正。

但真正的重點是難道出版業在這些電腦化的過程中沒有一個自動化的公司可以來檢查這樣TRIVIAL的錯誤嗎?還是其實沒有人做這類工具?或是這類公司軟體沒有市場不會有收益?但總是對出版社來說是有需求的啊?

好吧,下次有遇到出版社朋友再來請教他們一下。或是如果剛好出版業的朋友看到也可以告訴我一下。

CSS3 Transition的唯一事件:TransitionEnd

CSS3不是HTML5

在HTML5被大力的吹捧之後,只要是有關Web上的Animation就會被稱為是HTML5,這個問題目前普遍的出現在各大企業的行銷新聞稿裡面。常常需要解釋說這只是CSS3不是HTML5,不過講久了後來久懶的解釋了(這讓我想起之前3D相關議題很虹的時候,前公司的長官不斷的在各大會議裡面糾正大家的說法,就是其實不能講3D而應該是立體Stereo)。總之,CSS3跟HTML5完全就是兩份不同的Spec,只是剛好現在相關的討論都會放在一起講。

很fashion的CSS3 Transition的動態效果?

不知道是隨著歲月的演進還是Apple的功勞,現在的網頁開始出現了animation的潮流(之前Flash的風潮已經很久了,不過是屬於比較heavy的animation)。所以最近工作也有被要求加入一些實驗性質的animation,不過其實animation加的妙不如加的巧。比如近來Google+的簡潔社交圈的介面就非常獲得好評,一些很light-weight的DOM element animation。早期的作法基本上都是JavaScript硬幹,但其實仔細研究一下Google+,其實還是JS的Timer-based animation,大概是考量到跨瀏覽器的支援的問題,否則的話使用CSS3 Transition其實是可以得到比較順暢的animation。畢竟JavaScript的Timer是在一個sequential thread執行的方式,而且很容易被其他的JavaScript timer影響。如果是CSS3 Transition就可以使用到真正的native thread,甚至適當的時候還可以啟動硬體加速。不過我還是覺得只是為了有這個效果而加的話應該是沒什麼必要,只有在加入之後可以真正改進使用者經驗(UX)的時候才有意義,否則基本上反效果會被放大。

CSS3 Transition實務上的問題

在利用CSS3 Transition製作動畫的時候有一個很困擾我的問題。通常一段animation結束之後,你會需要做一些後處理,比如說被Zoom-In的圖片要換成解析度高一點的照片,或是要再啟動下一段animation(每個transition-end就等於是animation的keyframes。CSS3 Transition的Spec上有定義一個event:"transitionend",這個event裡面會帶有propertyName以及elapseTime,但是如果我設定了對 width / background / color 做animation,那這個event會發生三次,分別對應不同的屬性。就如下面這個例子(按一下方塊可以看結果):

Click me to animate

不過這其實就會產生一個問題就是你的後處理handler會被執行好幾次,有可能產生非預期的結果。有兩個方法解決:

  1. 有一個方法是每次設定transition就記住有改變的屬性,在收到每個event的時候去檢查看看是不是所有的屬性都已經結束,是的話才真正的去做後處理handler。不過之前這個方法有遇到一個問題,就是有些沒有改變的屬性也會收到對應的transitionend event,應該是Chrome12或13的時候會有問題,剛剛測試過Chrome14好像已經沒有這個問題了。
  2. 另外還有一個方法是用Timeout,也就是如果你的transition的時間是1s,那在設定屬性之後就立刻設定一個1秒的timeout去模擬一個transition-end event。由於transition的執行是native code,所以會很準時的做完frame animation,但timer會是JS layer,所以最多就是剛好在 transition結束後的那一刻timeout或者是稍微晚一點點,所以拿來模擬transitionend是不會有問題的。

雖然可以用模擬的方式解決,不過在以前實作callback的相關經驗通常會在單一事件的回傳之後都會在增加一個TotalComplete的事件,CSS3 Transition Spec實在應該把這東西加進去啊。

(image from http://www.zazzle.com/html5_css3_logos_tshirt-235055837924253380)

Javascript Object-Oriented Programming (1)

 

 

很不友善的 JavaScript 物件繼承寫法

最近一年以來又開始大量JavaScript的coding,距離從前做web已經過了十年了。離開學校之後,大部分的時間都在從事Windows Application開發。為了重拾兒時開發web的記憶,看了許多新的技術,不過最讓我覺得驚奇的是 JavaScript 的 OO 語法還是一樣的不友善啊。十年前做Web的時候只需要搞定 IE6 就可以了,現在就還要考慮IE6 / IE7 / IE8 / IE9 / IE10 / Chrome / FireFox/ Opera/ Mobile Browser 。現在的Web Developer真是太辛苦了。

當時最主要的JavaScript參考網站是 DynamicDrive(現在居然還活著耶),DynamicDrive居然還保留著那時候最紅的term:Dynamic HTML。當時書店很多書都在講 DHTML,現在已經都是 jQuery/Ajax的天下了。(不由自主的懷舊起來 XD) 在當時,我們就已經大量使用 iframe 來達到現在的 Ajax 想做到的事情,不過現在當然還是Ajax比較有效率。(雖說目前有些時候還是不得已會需要用 iframe,比如說 Cross-Site Script 的問題)

Prototype Inheritance

再看過了一些近代的JavaScript寫法之後,實在還是覺得JavaScript的OO非常的特別,很值得好好研究一下。所以我再回頭看了一下JS的Object的原理。

一般的JavaScript的Class定義的方式是這樣:

1
2
3
4
5
function Shape() // constructor of Shape class
{
    this.type = 'shape';
    this.area = 0;
}

這樣一定義之後,就會有一個 Shape 的 Function Object(阿,對!JS的Function本身是一個Object),Shape這個Function Object本身會帶有一個 prototype 屬性。這個prototype屬性就是JavaScript OO寫法的重點,所有在 prototype 中的屬性都會被 new 出來的 Object Instance 繼承(以Reference的形式)。

所以,如果要定義 Shape Class 的 instance method,就直接新增一個 function property 給 Shape 的 prototype(這個時候的 prototype 也是一個 Object)。

1
2
3
4
Shape.prototype.getArea = function() // instance method of Class Shape
{
    return this.area;
}

這時候如果create兩個 Shape 的 instance:

1
2
var rect = new Shape();
var triangle = new Shape();


rect 以及 triangle兩個 object 本身都有自己各自一份 type 以及 area 屬性,至於 getArea 則會 refer 到 Shape Class 的 prototype object 裡面的 getArea。 就是這個特性延伸出後面的JavaScript物件繼承方法

關於這個特性,另外還有兩個特別需要注意的地方:

    1. 第一個是關於instance method的定義以及記憶體使用量的問題

以上的範例可以改寫下面的code,執行結果會是一模一樣的。

1
2
3
4
5
6
7
8
function Shape() // constructor of Shape class
{
    this.type = 'shape';
    this.area = 0;
    this.getArea = funtion(){
        return this.area;
    }
}

唯一的差異是,用這種寫法的話,你create的object instance越多,耗費的記憶體就越多(相對於第一種寫法)。

如上圖,每個 instance 自己本身會有一個 getArea function 的實體,而前一種寫法則是會只有一份實體,所有呼叫都會用到 prototype 下的那份實體。所以如果你 define 的 class 會被大量生成的話可能還是要考量一下這一點。

    1. 第二個是name hiding 的問題
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Shape() // constructor of Shape class
{
    this.type = 'shape';
    this.area = 10;
    this.getArea = function(){ // this definition would hide
                               // the one with the same name in Shape.prototype
        console.log('instance');
        return this.area;
    };
}
Shape.prototype.getArea = function()
{
    console.log('prototype');
    return this.area;
}

當呼叫某個物件下的function或是讀取某個變數的時候JavaScript的搜尋規則是先看自己本身的屬性有沒有符合要存取的 function 或是 變數名稱,如果沒有的話才會再往 prototype 去找,所以像上面的這種寫法,function Shape 裡面的 getArea 就會直接蓋掉 prototype 下的那個 getArea。

物件繼承的寫法

所以一般最原始的JavaScript的繼承寫法,假設我們把Shape當做base class,Rectangle是繼承自Shape的sub-class,那個程式碼應該會是這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function Shape(type) // Base Shape class
{
    this.type = type;
    this.w = 0;
    this.h = 0;
}
Shape.prototype.getArea = function()
{
    return this.area;
}
 
function Rectangle(w, h) // The derived class inheriting Shape
{
    this.superclass('rectangle');
    this.w = w;
    this.h = h;
    this.area = w * h;
}
 
Rectangle.prototype = new Shape(); // Set the derived class prototype
                                   // object as the parent class
                                   //  object istance
 
// 在建立與parent的連結之後可以新增 Rectangle
// 自己本身的 method 或是 其他屬性
Rectangle.prototype.getWidth() = function(){
    return this.w;
}
 
Rectangle.prototype.superclass = Shape; // Not necessary
                                        // just for constructor chaining
Rectangle.prototype.constructor = Rectangle; // 根據實際測試,這可能不需要!

其中最重要的一個步驟就是 create 一個 Shape object instance 然後 assign 給 Rectangle 的 prototype。

Rectangle.prototype = new Shape();

就如前面所說,function object一定會有一個prototype object,而 JavaScript 的物件繼承就來自於將每個 subclass 的 prototype 串接到 super class 的 object instance之後,而每個function呼叫或讀取屬性的時候會先往 prototype 裡面找,這麼一來就達成了物件繼承的基本樣子了。

Rectangle.prototype.constructor = Rectangle;

至於上面這一行,是為了把 constructor 修正為目前 class 的 constructor,大部分講到JavaSCript OO的書都會提到要做這件事情。但是我拜讀了大師Douglas CrockfordClassical Inheritance in JavaScript文章之後,發現他沒做這個動作。於是我稍微測試了一下,看來的確是可以不用。

Constructor Chaining

Rectangle.prototype.superclass = Shape;

一般的OO,通常會在sub-class的constructor自動去呼叫superclass的constructor,但是JavaScript沒有幫你做這件事情。不過由於這個動作非常的必須且重要,所以我們就直接把 superclass的constructor給keep下來成一個屬性,這樣我就可以去呼叫他了。一般書上都沒有提到這種chaining的寫法會有一個問題,就是繼承的深度大於兩層,那這個方法就完了。由於前面提到的name hiding的問題,所以如果你這時候有一個Square class去繼承自Rectangle,那這個時候的prototype的sperclass就會變成是Rectangle。那當從Rectangle的constructor去call this.superclass的時候就會呼叫到自己然後進入無窮遞迴的地獄裡!
解決方法大概就是最原始的寫法最安全:

Shape.call(this, 'rectangle');


看完了原理之後,我們都知道程式工人進步的動力來源是抄襲研究有名的source code,我們就來看看幾個有名的JavaScript函式庫是如何包裝class繼承這一塊吧。

Douglas Crockford’s
Classical Inheritance in JavaScript

第一個一定要先來看一下大師的寫法,非常的簡潔不好懂,只有短短幾行,但是該做的事情都做完了,而且對於繼承體系中向上層呼叫的事情也包裝的很巧妙:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};
 
Function.method('inherits', function (parent) {
    var d = {}, p = (this.prototype = new parent());
    this.method('uber', function uber(name) {
        if (!(name in d)) {
            d[name] = 0;
        }
        var f, r, t = d[name], v = parent.prototype;
        if (t) {
            while (t) {
                v = v.constructor.prototype;
                t -= 1;
            }
            f = v[name];
        } else {
            f = p[name];
            if (f == this[name]) {
                f = v[name];
            }
        }
        d[name] += 1;
        r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
        d[name] -= 1;
        return r;
    });
    return this;
});

我們用一個深度為三層的例子來解釋這段很巧妙的包裝方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Base X
function X(value){
    this.init(value);
}
X.method('init', function(value){
    this.value = value;
});
X.method('getValue', function(){
    return this.value + ' from X';
});
 
// Y derived from X
function Y(value){
    this.uber('init', value);
}
Y.inherits(X);
 
Y.method('getValue', function(){
    return this.uber('getValue') + ' from Y';
});
Y.method('getValue2', function(){
    return 'getValue2 from Y';
});
 
// Z derived from Y
function Z(value){
    this.uber('init', value);
}
Z.inherits(Y);
 
Z.method('getValue', function(){
    this.uber('getValue2');
    return this.uber('getValue') + ' from Z';
});
 
var z = new Z('cool code');
console.log(z.getValue());

上面這段code的輸出結果是:

cool code from X from Y from Z

來拆解一下,首先針對Function這個object擴充了一個method的函式用來定義之後的class instance,之後就可以不用再寫很長的 function.prototype.method。並且後面也直接利用這個function再針對Function擴充了一個inherits函式。inherits的部分其實跟這樣寫是一樣的:
Function.prototype.inherits = function (parent) { ... };

inherits的實作第一部份很單純,就是create一個superclass的object instance塞給目前的prototype。
p = (this.prototype = new parent());

第二個部分,uber的實作方式很有趣,之前提到的name hiding的問題,透過uber你就可以往superclass呼叫到同名的函式,大概可以想像成是一種 super 語法的實作。先把它拆解成兩種cases來看:

  • 第一種case是單純的呼叫superclass的某一個function (getValue2),而這個function不會再繼續在呼叫uber。由於一開始 d 是空物件(行7,所以d['getValue2']會等於0,於是行13的if判斷會走到else,之後由於name hiding的關係,行21會被evaluate為true,於是拿到parent的getValue2。之後由於這種case很單純,所以行25、27在這裡沒有意義,可以直接忽略,之後getValue2就會在行26得到結果之後回傳。
  • 第二種case是call到superclass的 getValue 之後,還會繼續往上呼叫 getValue的 chaining call,一開始當call Z::getValue的時候,走的路跟第一種case一樣,但是這時候行25跟27不能忽略,於是行25會設定 d['getValue'] = 1,之後 f = Y::getValue,行26會執行Y::getValue,由於Y的getValue還會繼續call uber去呼叫X的getValue,所以會產生一個遞迴呼叫,這時候行13的t=1,所以行14~行17的while loop會往上走一層到然後行18拿到的f就是 X::getValue,之後執行完行26之後d['getValue']就變成1,在回到Y的call stack,於是d['getValue']變成 0,最後結果回傳到行33作為console.log的參數。這一段幾乎沒什麼人解釋,Crockford的網頁裡面沒有解釋,John Resig(jQuery作者)的Pro JavaScript裡面有提到這段code,也沒有多做解釋。不過我覺得這段code一開始看其實會不太知道在做什麼,所以特此解釋並做下筆記 :)

看完之後,對於大師就是佩服啊,幾行code就搞定這個麻煩事。

Prototype.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
var Class = (function() {
  function subclass() {};
  function create() {
    var parent = null, properties = $A(arguments);
    if (Object.isFunction(properties[0]))
      parent = properties.shift();
 
    function klass() {
      this.initialize.apply(this, arguments);
    }
 
    Object.extend(klass, Class.Methods);
    klass.superclass = parent;
    klass.subclasses = [];
 
    if (parent) {
      subclass.prototype = parent.prototype;
      klass.prototype = new subclass;
      parent.subclasses.push(klass);
    }
 
    for (var i = 0; i < properties.length; i++)
      klass.addMethods(properties[i]);
 
    if (!klass.prototype.initialize)
      klass.prototype.initialize = Prototype.emptyFunction;
 
    klass.prototype.constructor = klass;
    return klass;
  }
 
  function addMethods(source) {
    var ancestor   = this.superclass && this.superclass.prototype;
    var properties = Object.keys(source);
 
    if (!Object.keys({ toString: true }).length) {
      if (source.toString != Object.prototype.toString)
        properties.push("toString");
      if (source.valueOf != Object.prototype.valueOf)
        properties.push("valueOf");
    }
 
    for (var i = 0, length = properties.length; i < length; i++) {
      var property = properties[i], value = source[property];
      if (ancestor && Object.isFunction(value) &&
          value.argumentNames().first() == "$super") {
        var method = value;
        value = (function(m) {
          return function() { return ancestor[m].apply(this, arguments); };
        })(property).wrap(method);
 
        value.valueOf = method.valueOf.bind(method);
        value.toString = method.toString.bind(method);
      }
      this.prototype[property] = value;
    }
 
    return this;
  }
 
  return {
    create: create,
    Methods: {
      addMethods: addMethods
    }
  };
})();

Prototype的class inheritance在ㄧ開始有註解是源自Alex Arnell’s inheritance implementation。這一長串的Class的邏輯在仔細的追蹤之後,我會覺得臻的有點奇妙,就整段的重點在於addMethods裡面在處理有宣告$super為參數的function,目的是為了達到繼承體系中往上串接的能力(其實就是 Crockford的uber想做的事情),不過寫得太過複雜,包裝方式有很高的overhead。

行8到行28基本上就是先建立一個空的klass主體,接著以Object.extend將Class.methods擴充至klass。Object.extend做的事情基本上只是把第二個參數中的所以屬性複製一份給第一個參數:

  function extend(destination, source) {
    for (var property in source)
      destination[property] = source[property];
    return destination;
  }

行16到20,依照基本原則建立一個superclass的object instance作為目前klass的prototype,之後有趣的是在行28很規矩的依照原則做了constructor的調整。基本上這樣已經完成了基本的繼承架構。不過在這邊的行32到59這一個addMethods做了不少事情需要特別解釋一下。

在行36到41這一段,實在令人百思不得其解,一開始的if條件就永遠不可能為真,所以這一段code放在這裡的意義是?小弟資質駑鈍,希望可以有人為我解答一下。

行45到54這一個部分,一開始先把function toString之後再用regular expression檢查第一個參數是不是$super,是的話就會進入一個複雜的過程:

行48到50 – 建立一個匿名函式,並透過closure技巧將property帶入至m,比如說如果有一個Say function的話,那這個匿名函式就隱性的變成 function() { return ancestor[“Say"].apply(this, arguments); };。

行50 – 對上面帶出的匿名函式做一次wrap,於是就作出一個匿名函式帶有parent作為第一個參數:

  function wrap(wrapper) { // wrapper 是目前的函式
    var __method = this; // this 是匿名函式
    return function() {
      var a = update([__method.bind(this)], arguments);
      return wrapper.apply(this, a);
    }
  }

wrapper這時候是subclass的function,而__method就是上面create出的匿名函式,然後因為bind(this),所以最後呼叫時候以目前物件為scope去呼叫上一層的function。

用法上的話,以prototyp.js官方文件的範例來看,用法上算是很直覺,prototype.js自己本身也是利用自己的繼承架構來實作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// properties are directly passed to `create` method
var Person = Class.create({
  initialize: function(name) {
    this.name = name;
  },
  say: function(message) {
    return this.name + ': ' + message;
  }
});
 
// when subclassing, specify the class you want to inherit from
var Pirate = Class.create(Person, {
  // redefine the speak method
  say: function($super, message) {
    return $super(message) + ', yarr!';
  }
});
 
var john = new Pirate('Long John');
john.say('ahoy matey');
// -> "Long John: ahoy matey, yarr!"

還有一點關於prototype.js的source code是沒有提供minified的版本,並且從裡面很多source code的寫法來看,minify prototype.js應該是會有一些問題,比如說while或是if允許單行本體而不需要以{}包住,但是minified之後很可能就整個爛了。另外現在一般都會建議使用CDN(Content Delivery Network)提供的public library,其中一個好處是如果有cache的話可以很快的載入,而且通常CDN的網路速度以及穩定性應該都會比你自己的好。目前比較常用的是Google CDN

ExtJS

接著可以看一下我覺得實作得非常完整的一套JavaScript library, ExtJS。Sencha已經release ExtJS4的版本了,不過這邊還是看一下我使用比較多的ExtJS3的版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
extend : function(){
    // inline overrides
    var io = function(o){
        for(var m in o){
            this[m] = o[m];
        }
    };
    var oc = Object.prototype.constructor;
 
    return function(sb, sp, overrides){
        if(typeof sp == 'object'){
            overrides = sp;
            sp = sb;
            sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);};
        }
        var F = function(){},
            sbp,
            spp = sp.prototype;
 
        F.prototype = spp;
        sbp = sb.prototype = new F();
        sbp.constructor=sb;
        sb.superclass=spp;
        if(spp.constructor == oc){
            spp.constructor=sp;
        }
        sb.override = function(o){
            Ext.override(sb, o);
        };
        sbp.superclass = sbp.supr = (function(){
            return spp;
        });
        sbp.override = io;
        Ext.override(sb, overrides);
        sb.extend = function(o){return Ext.extend(sb, o);};
        return sb;
    };
}()

ExtJS的繼承實作囉及其實很簡單,基本上就是把基本原則做一次,如果需要往繼承體系的上層呼叫也就是很簡單的直接call superclass的function然後以apply或call改變this scope:

1
2
3
4
5
6
7
8
9
10
11
12
Ext.Base = Ext.extend(Object, {
    member: 0,
    constructor: function(){
    }
}
 
Ext.Sub = Ext.extend(Ext.Base, {
    member: 0,
    constructor: function(){
         Ext.Sub.superclass.apply(this, arguments);
    }
}

簡單易用,而且寫法上也比較有模擬出類似一般class繼承的寫法。不過缺點是商業使用是要錢的喔。

jQuery

很著名的jQuery本身並沒有提供class繼承的寫法,但是其作者John Resig倒是有一篇文章: Simple JavaScript Inheritance提供了一個做法去實作出對每個instance function可以去呼叫對應的superclass版本。

結論

總結來看,我個人比較欣賞的是Douglas Crockford提供的方法,很簡短的code達到目的,不過就是使用上比較不直覺。而Prototype.js是我去年用的還滿多的一套JS library,好處是對於DOM element的包裝很完整,壞處就是overhead太高,在一般普遍的benchmark中往往是敬陪末座,在class的用法上基本上也還算是易用,但是背後的實作太過複雜,在不管是空間與時間的複雜度來說都顯得比較高。總結來說,我個人會選擇ExtJS,如果不考慮錢的話,或者是把Douglas Crockford的方法擴充到一個你慣用的library裡(反正JavaScript的擴充你想動就可以動)。


本來只是想要做個筆記,想不到寫這篇寫了好幾個禮拜。

圖片來源:

  • http://www.w3avenue.com/2010/04/04/js-class-ruby-style-object-oriented-javascript/
  • http://www.zazzle.com/