2012年6月28日 星期四

jQuery 原始碼解讀 (II)

上次筆者在 jQuery 原始碼解讀 (I) 分享了關於 jQuery 進入點的見解,如果以 jQuery 原始碼結構來說的話上一次的討論在於分析了 jQuery 的 intro 以及  outro。在進入點之後,我們就可以來進入 jQuery 的核心:core.js 部分了。

由於 core 本身帶著非常多的碼,筆者想針對幾個比較重要的點來解讀,所以我們先把它簡化成這樣:


從上面的 code 來看,我們一般在使用 jQuery 時,最常用的是 $("selector"),而 $ 其實就等於 window.jQuery (core 這邊其實有一段 code 在處理 window.jQuery / window.$ 可能會有 naming conflict 的問題,我們這次先跳過)。 所以 $ 實際上會是執行

new jQuery.fn.init( selector, context );

這邊呢,jQuery.fn.init 是 jQuery.prototype 中的一個 function,而由於這個敘述:

jQuery.fn.init.prototype = jQuery.fn; // jQuery.fn = jQuery.prototype

使得 jQuery.fn.init 也成為 jQuery 的 constructor。這樣一來的話,基於 prototype 繼承的特性,就可以透過在 jQuery.fn 上動態新增 function 來達到擴充的功能。也就是說由於 JS 的物件都會有一個 reference 指向 prototype object,因此在後面新增到 prototype 的 function 也會讓已經生成的 object 具有該新 function。

而以 jQuery 為基礎來開發的人一定很清楚 jQuery 的可串接性 (chaining) 的寫法。比如說這一段 code:

其實可以寫成:

之所以可以這樣寫是因為幾乎每一個 jQuery function 都會 return this; // i.e. jQuery object。當然會有例外,比如 .val() 就不行。 也就是說當要擴充 jQuery.fn 的時候也必須要把 chainability 考慮進去。

再來有一點,我覺得很少人討論,我之前在用 jQuery 的時候,每次下 selector,jQuery 都會回傳一個 array,在 inspector 裡面都會看到 jQuery.fn.jQuery.init[n] ,每次我都覺得很奇怪,但是稍微看一下 code 又看不出為什麼。後來我很認真地研究終於發現原來只要一個 object 裡面有 length 這個屬性,同時又有 splice 這個 function 那行為就會看起來很像 array 但是並不是真的 array instance。

很容易驗證,在 console 裡面這樣打就可以看得出來了:

var fakearray = { length: 0, splice: function(){} };
undefined
fakearray
[]
fakearray.splice = null;
null
fakearray

Object
不知道是不是筆者太嫩,我看網路上在討論 jQuery source 都沒有講到這個,可能大家都知道,只有我不知道 XD

好的, jQuery 原始碼解讀第二趴就先到這裡了。 待續...

jQuery 系列文章:

2012年6月26日 星期二

使用 iDoneThis 來增進你的生產力

身為一個 Programmer,我們總是有滿滿的工作在身上,要怎麼樣增進自己的生產力呢?有關於幫助增進生產力的討論很多,像是 GTD 就是其中之一,不過除了 TODO List 之外還有沒有其他的方法可以幫助我們呢?

過去幾個禮拜我使用了 iDoneThis 這個服務,iDoneThis 的概念很簡單,每天這個 service 會在你指定的時間寄信給你,你只要回覆一封 e-mail 就可以記錄自己一整天做了些什麼事情。每天記錄的結果,就會列在如下圖所列的行事曆:

iDoneThis

iDoneThis 的概念看似很簡單,不過對我來說真的有增進生產力的效果,到底是為什麼呢?

  1. 它讓我可以很自然的紀錄每天做的事情。收發 e-mail 是我每天都一定要做的事情,所以把記錄這件事情融入在原本的 workflow 當中是在順暢也不過了。我不用另外開什麼軟體,也不用打開我的筆記本,我只要像平常每天要做的事情一樣:打開收件夾、回信。這樣就完成了記錄的工作。
  2. 它讓我思考更多關於事情的先後次序。透過每天的紀錄,我可以再檢視一次今天做了什麼,想想明天可以做什麼,再調整一下自己的 TODO List。
  3. 它會讓我更有動力完成每天應該做的事情。人性是很奇妙的,雖然這只是一封信,不過這封信會讓我再回信之前想想是不是還有什麼事情其實自己可以今天做完而沒做的,常常因此我每天的 TODO List 可以減少了一項到兩項。

你是不是也希望增進自己的生產力呢?個人版的 iDoneThis 是免費的,如果想要跟你的團隊一起使用,那麼每個月一個人只需要美金三塊。就從今天開始試試看吧,或許你也會跟我一樣喜歡上這個簡單但是相當有用的服務!

2012年6月17日 星期日

jQuery 原始碼解讀 (I)

這次想要開啟一個 jQuery 原始碼解讀的文章系列,原因是因為原本以為我已經算是了解 JavaScript 了,但最近遭受打擊,於是想要再重新學習 JavaScript,我想最好的方法就是看懂大師的 code。 jQuery 應該也算是公認的大師作品了。所以就選定 jQuery 來研讀好了 :)

如果你也曾經自認為懂 JavaScript,建議您看看你其實並不懂JavaScript,評量看看您是否真的了解,通常看過之後大部分人會覺得自己知道,但我必須說除非你確認你有開發並應用到相關的知識,不然只是從書上看過並不代表你真的了解!尤其 JavaScript 還有很多出乎正常邏輯外的運算結果!一起來精進 JavaScript 吧。 這次就讓我們先從 jQuery 的進入點來看吧: jQuery 的進入是透過 self executing anonymous function (or self invoking anonymous function) 的 pattern。

    function(window, undefined){} 這定義了一個 Anonymous function

之後透過括號 () 包住 Anonymous function: (function(window, undefined){}) 取得 evaluate 過的 Anonymous function 再透過 () 去執行 Anonymous function

   (function(window, undefined){})();

這邊有個重點是一定要先用 () 包住 Anonymous function ,否則會得到 syntax error。 Paul Irish 的 10 things I learned from jQuery 影片裡面關於這一點似乎說錯,他說技術上這一點是 optional ,但很明顯的不是如此。

不過另外他提到的 Facebook 的 source code 裡面有看到一些相同作用但不同語法的方式是:

  !function(window, undefined){}();   或    +function(window, undefined){}();

這部份就是正確的,基本上都只是需要讓 Anonymous 先產生定義,再透過後面的 () 執行它。

到目前為止僅是語法的部分,但在這之外 jQuery 使用這 self invoking anonymous function 的 pattern 一定有其意義。最重要的意義我想應該就在於 John Resig 自己的書 Secrets of the JavaScript Ninja 裡面所提到的 Library Wrapping。透過 Anonymous function 的 local scope 將 library 本身定義的 variables 以及 functions 限制在 local scope 裡面,這麼一來不會影響全域的命名空間,同時也避免 jQuery 自己的變數以及其他定義被外界影響。

了解語意之後,再來看看 jQuery Anonymous function 的參數 window 以及 undefined 的意義。第一個參數 window 很容易理解,在呼叫 Anonymous function 的時候傳入 window,因此而將 window 從 global 變數轉換成在 local scope 的一個 reference。雖然說不傳也可以直接存取 window 這個變數,但這邊轉換成 local scope 的 window reference 之後可以提升一些效能。原理大概是在 JavaScript 裡面存取變數的時候會先在 local scope 找, 找不到的話再往上找 global scope。這邊的作法就會讓 window 的存取少了一層,因此可以增進效能。不過那也是因為 jQuery 會大量用到 window,如果你的 code 裡面沒有大量用到應該也是不用這樣做 :)

至於 undefined 這個參數,首先第一個特別的點是在呼叫的時候沒有傳第二個參數,所以透過 JavaScript 的語法讓第二個參數 "underfined" 帶入 window.undefined。這樣做的原因我聽過兩個,第一個是說由於 undefined 是一個 global 變數,有可能被改掉,所以必須透過這個方式綁定 undefined 的值。這樣一來就不怕 undefined 的值被突然改掉了。第二個說法是,有些瀏覽器的 undefined 不叫 undefined,所以透過這個方法可以強制讓 undefined 都是 undefined。基本上兩個方法都合理,但我也都有點存疑。第一個說法讓我存疑的是在於那為什麼就不管其他的 global variables?而只處理 undefined?也許是因為 jQuery 真的只用到 undefined 吧。再來是,我實驗了把 window.undefined 的值改掉,結果發現改不掉,看來現在瀏覽器(筆者使用 Chrome 19.0.1084.56 測試)很聰明的會保護一些特別的值。所以目前就我看起來真不是理由 XD 第二個說法存疑的點是真的有瀏覽器沒有支援 window.undefined ? 也許真的有,但至少我還沒遇過。

好的, jQuery 原始碼解讀第一趴就先到這裡了。 待續...

參考:
jQuery, John Resig
10 things I learned from jQuery source, Paul Irish
Speed up your JavaScript, Nicholas C. Zakas
Secrets of the JavaScript Ninja, Jogn Resig

2012年6月15日 星期五

看 Treehouse CEO 怎樣遠端管理公司

Remote Control, Television - TV-controller

對於軟體業來說,最大好處之一就是你可以遠端的工作。著名的軟體公司 37signals 的員工就是散佈在各地工作,或許有人會覺得那只是特例而已,前幾天 Treehouse 的 CEO Ryan Carson 寫了一篇 blog 來講他是怎麼樣在遠端管理公司的辦法

Treehouse 是一個讓你做線上學習的網站,員工大約有 40 人。Ryan Carson 住在英國,主要辦公室在 Orlando,其他的員工則分散在世界各地。儘管兩地有著八小時的時差,Ryan 依舊遠端管理了公司將近兩年,創造了超過三百萬美金的收入,下面是他怎樣管理公司的方法。

把 HR 外包

透過 TriNet 外包 HR 相關事務(薪水、保險、稅… 等等),關於人事方面有太多的雜務要處理,把這些外包出去給 TriNet 大概一個人每個月要花 100 美金左右,不過很值得。

雇用財務管理/業務經理

如果你想要遠端管理你的公司,那麼你還是得要一個值得信賴的人坐在辦公室。當然對於一個新創公司來說,雇用這樣的一個人可能會負擔不起,但是當公司的現金流很不錯的時候應該要找一個人來幫忙處理公司每天的事務。

用 Campfire 當公司聊天室

遠端工作的時候需要一個地方來討論一些比較沒有組織的想法或是些隨機的話題,在 Treehouse,他們使用了 Campfire 來當做公司的聊天室,他們開的聊天室大概有:

  • Chiggity Chillin (company wide hangout)
  • Product Team
  • Dev Team
  • Firehose (commits to the Treehouse GitHub repo and deploys to Production)
  • etc

試著用不同的方式溝通

P2 Theme

Campfire 對於一些 long-term 的討論來說是比較沒組織的,為了讓 Product Team 可以更加有效率的溝通,Treehouse 安裝了一個 private 的 wordpress 來幫助討論,他們使用了 P2 Theme

使用 Go to Meeting + Google Docs

如果真的需要 meeting 的時候,使用 Go to Meeting。雖然 Skype 也是可以作多人的 meeting,但是還是不夠穩定。

Google Drawing

在需要 live editing 的時候,他們用 Google Docs (尤其是 Google Drawing) 來當虛擬白板。

當然無論如何遠端 meeting 還是比不上直接面對面的談話,所以每年還是會到 Orlando 四次來進行一個四天的全公司會議。

用視覺化的方法組織

Treehouse trello

在 Treehouse 裡面,所有的專案現在都使用 Trello,之前他們使用 Asana,不過相較之下 Trello 更視覺化,也更容易可以掌握事情的進度。

你平時也是可以遠端工作嗎?或許文章中的方法可以讓你更有效率!

來源:How I manage 40 people remotely

(image via espensorvik, cc license)

2012年6月13日 星期三

幫你的網頁加上 backbone(.js) 吧(四)!

今天要帶給大家的是 Backbone.js 的 Collection 的使用,這是「幫你的網頁加上 backbone(.js) 吧」 系列的第四集,文章部分取材自於 Joe Zimmerman 的 Javascript Blog 影片教學單元。看影片學寫程式是一件非常棒的事情,但若想快速複習影片中的重點,文字會是更有效率的方式,這也是這系列文章存在的初衷;如果有翻譯不周的地方,也懇請不吝指正。

在開始之前可以到 JSFiddle 取得範例程式碼並一邊閱讀底下的介紹。
環境的基本與過去大致相同:


Collection 簡單來說就是一組 Model 的陣列版本,如果有一堆 Model 需要被當做陣列(或類似的有序資料結構 - ordered set)來操作,而更重要的是,Collection 可以無縫的整合 RESTful web service,大大減低與後端溝通的阻礙。此外,Collection 可以直接對監聽  Collection 本身或 Collection 裡的 Model 所發出的各種事件(event),例如 change, add, remove 等等。
來看看底下這個例子,假設我們有一種 Model 叫做 Person:


如果我們想要有一個代表一群人的物件,可以用 Collection 建出代表一群人的 Collection: people,只要在建構的屬性裡面指定 Model 為 Person 即可:


產生一個 Person 或 People 的方法就像我們過去所知道的方法類似:


從 People (人群) 增加一個 Person (人)的方法:

people.add(person);

或是一些人

people.add([{name:"Bob"}, {name:"Jim"}]);

除此之外,也可以把 Collection 當做堆疊(stack),透過 push/pop 來操做:

people.push([{name:"Bob"}, {name:"Jim"}]);
people.pop();

也可以用 reset 來新增 Model。reset 和 add 唯一的差別在於 reset 會清除掉原本的所有 Model

people.reset([{name:"Bob"}, {name:"Jim"}]);
/* 或是 */
people.reset([{name:"Bob"}, {name:"Jim"}], person);

想刪除一些人也沒問題:

people.remove(person);

值得注意的是上面的 add, reset 和 remove 都會觸發本身的事件(add, reset, remove event),我們可以用 on 這方法來監聽這些 event,例如

people.on('reset', function(){});

還有一些存取 Model 的方法,包括 at 和 getCid 等。at 這方法可以直接依照 Collection 裡面的 Model 生成的順序讀取,像是:

people.at(1);

Collection 的每一個 Model 都會有一串代表 Model 的 Cid,編號方式是 c 加上數字,例如 c1, c2。可以透過 getCid 來取出 model

people.getCid('c1');

除了上面方法來存取每個 Model 之外,也可以用 Models 來直接存取 Collection 的內容,例如:

console.log(people.models);

當然,要把整個 Collection 轉成 JSON 也只要呼叫一個方法即可:

people.toJSON();

若要讓 Collection 裡面的 Model 有排序,可以定義 comparator 這個屬性:


上面的例子,我們傳進了兩個 person, person2,取出 person 的 name 屬性作比較 如此一來 collection 的 getter/setter 就會按照 name 的排序來增減。 可以透過呼叫 sort() 來取強迫 collection 排序:

people.sort();

Collection 有一些特殊的過濾方法,其中一個是 pluck,可以從 Collection 中取出每個 Model 的某相同 key 的值的陣列。
例如上例中的 people 有三個 person,我們可以取出一個三個 person name 的 array:

people.pluck('name');

結果是

['Joe', 'Bob', 'Jim']

另外一個過濾方法是 where,可以藉由指定某種物件,找到 Collection 當中包含該物件的陣列。 假設我們有一個物件 friends:


透過 where:

var musketeers = friends.where({job: "Musketeer"});

我們可以得到 musketeers.length 的長度是3。

最後,是結合 RESTful web servcie 的操作,可以透過指定 url 屬性來對應到 RESTful API,例如:


上面這個例子的 People 會從 '/friends' 這個位置取得 JSON 資料,利用 guys.fetch() 就可以把資料讀進 Collection 裡面,而不必再另外 GET 資料並逐筆塞入 Collection,大大的簡化了存取或操作資料所需的瑣碎流程,是不是很棒呢?

Collection 是一個非常實用的 Backbone 物件,相信看完上面的例子之後,對於 Collection 的使用已經不再陌生。如果還想知道更多細節,歡迎參考官方說明文件,相信能讓您寫起 code 來如虎添翼!

Happy Coding!


延伸閱讀



2012年6月1日 星期五

Django Sitemap

之前的 SEO 幼幼班 (二)當中,我們提到了登錄 sitemap 的重要性。sitemap 主要是敘述了網站的結構,可以藉由 sitemap 使得搜尋引擎更容易理解網站的內容,藉此幫助搜尋引擎對於網站的索引。這邊我們來告訴大家要如何透過 Django 這個強大的 web framework 來創建動態的 sitemap。

sitemap 怎麼建?

sitemap 是個 xml 檔,格式可以參照 sitemaps.org的描述,基本上來說就是要知道每個 url 的位置、上次更新時間、更新頻率以及權重等等的資料。對於靜態網頁來說,sitemap 可以用人工慢慢的建出來,因為靜態網站不會變動,因此建一次後提交即可。但是對於動態網站或是規模較大的網站來說,可以考慮像是 http://www.xml-sitemaps.com/ 這種 sitemap generator。它會透過 crawler 的方式抓取你的網站,並且建立相對應的 sitemap。

Django sitemap

用手工或是 sitemap generator 的方式生成 sitemap 固然是種方法,但是常常我們的網頁會不斷的新增內容,這個時候用 sitemap generator 定期掃一次的方式可能就不會是那麼即時的更新。對於 Django 這個優秀的 web framework 來說,自動建立 sitemap 當然不會是問題。步驟如下:

在 settings.py 當中加上:

接著就可以訂定 sitemap 當中的成員,假設你今天的網站是個以 blog 為主的網站,那麼你的 views.py 可能會這麼寫:

在這邊我們直接繼承自 Sitemap,設定好 changefreq 根 priority。item 是代表著所有要加入到 sitemap 當中的資料,lastmod 代表著最後更新的日期,location 則是對應到網站的 url。如果你在網站當中有多項資料,那麼只要分別實作不同的 Sitemap 即可。

最後,在 urls.py 當中加入:

這樣就可以完成 sitemap 的創建。

結論

看到 Django 可以那麼輕易的創建出 sitemap 是不是很讓人心動呢?其實 Django 有更多好用的 applications,如果對於 Django 有興趣的人,歡迎參加我們 6/9 以及 6/10 的 PyCon Taiwan,和大家一起了解 Python 以及 Django 的奧妙!

Related Posts Plugin for WordPress, Blogger...