天天看點

《D3.js資料可視化實戰手冊》—— 1.4 了解D3風格的JavaScript

本節書摘來異步社群《d3.js資料可視化實戰手冊》一書中的第1章,第1.4節,作者: 【加拿大】nick qi zhu,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

d3.js資料可視化實戰手冊

對于那些習慣了過程式或者面向對象式的javascript風格的人來說,他們會感覺d3使用函數式的javascript程式設計風格有一些奇怪。本節會涵蓋一些javascript中函數式程式設計最根本的功能性概念,以便對d3有個基本的了解,将來可以用d3的風格來編寫可視化工程的代碼。

在浏覽器中打開下面檔案的本地副本。

現在讓我們嘗試更深入一點,了解一下javascript函數式方面的内容。請看下面的代碼段,

這段代碼在頁面上生成了下面圖檔中的示例。

《D3.js資料可視化實戰手冊》—— 1.4 了解D3風格的JavaScript

盡管非常簡單,但是不可否認,這段示例中的代碼跟d3風格的javascript非常相似。這不是巧合,在javascript程式設計範型中,這叫做函數對象。跟很多有趣的話題一樣,這個話題也能寫一本書。不過在本節中,我會嘗試盡量多講一些這種特殊範型的東西,好讓不了解d3文法的讀者也能建立這種風格的庫檔案。正如d3的維基頁面上所講的那樣,這種函數式程式設計風格給d3帶來了很大的便利性。

d3的函數風格,使得多種元件插件之間的代碼重用成為現實。

d3維基(2013年8月)

函數即對象

javascript中的函數是對象。跟其他的對象一樣,函數隻是鍵值對的一個集合。函數對象跟普通對象的差別就是,函數可以執行,函數還帶有兩個隐藏的屬性,即函數上下文和函數代碼。這兩個隐藏屬性有時候會給你一個大大的“意外驚喜”,如果你有着很深的過程式程式設計背景,這點可能更明顯。不過這也是我們最需要注意的關鍵點了,要了解一下d3使用函數的奇怪方式。

圖像說明文字javascript的大部分特性都顯得有些不夠“面向對象”,不過在函數對象這方面,javascript跟其他語言相比較應該更勝一籌。

現在我們心裡有了這樣的概念,那就再看一遍這段代碼。

在a、b和c行,我們可以看到instance、headline和description都是simplewidget這個函數對象的内部私有變量。可是render函數卻是instance對象的一個方法,并且被定義為對象字面量。函數本身也是對象,它可以存儲在對象/函數、其他變量、數組裡,也可以作為函數參數。運作simplewidget的結果就是i行所寫的,傳回一個instance對象。

for(var i = 0; i < 10; i++){

for(var i = 0; i < 2; i++){

}

}<code>`</code>

圖像說明文字對于上面這段代碼,你可能覺得它會列印20個數字。其實在javascript裡,這段代碼會陷入到無限循環。因為javascript沒有實作塊級别的作用域,是以裡面那層循環的i跟外面那層循環的i是同一個變量。于是裡面的循環改變了i的值,導緻外面的循環永遠不會結束。

跟流行的原型程式設計中的僞類模式相比,這樣的模式通常被稱作函數模式。函數模式的優點是提供了更好的資訊隐藏和封裝。因為隻能通過靜态作用域規則裡限定的那些嵌套定義的函數來通路私有變量(我們示例中的headline和description),是以simplewidget函數傳回的對象就更加靈活,也更加健壯。

如果我們用函數式風格建立對象,并且該對象所有的方法都沒有用this,那這個對象就是持久(durable)的 1。持久對象就是許多功能行為的集合了。

instance.headline = function (h) {

if (!arguments.length) h; // &lt;-- g

headline = h;

return instance; // &lt;-- h

};<code>`</code>

你可能會問,g行的這個arguments從哪裡來的?這個例子中從來沒有定義過它。其實這個arguments是内建的隐藏參數,并可在函數執行時直接使用。arguments是一個數組,它包含了所在函數的全部參數。

圖像說明文字實際上,arguments本身并不是javascript的數組對象。雖然它有length 屬性,并可以用索引下标通路每個元素,但是它沒有javascript中數組對象的那麼多方法(比如slice、concat)。如果你想在arguments上使用javascript數組對象的方法,需要這樣:

var widget = simplewidget({color: "#6495ed"})

console.log(widget.headline()); // 輸出"simple widget"<code>`</code>

這裡你可以看到headline在參數不同的情況下,可以分别作為setter和getter(指派操作和取值操作)。

函數級聯調用

這個例子另一個有趣的地方是函數的級聯調用。這也是d3庫提供的一個主要的函數調用方式,因為d3庫中的大多數函數都設計成了這種鍊式的結構,以便能提供簡潔的、上下文連貫的程式設計接口。如果你了解可變參數函數的概念,就很好了解這個了。可變參數函數(比如headline函數)能同時作為setter和getter,當其作為setter時,傳回instance對象,這就使得你可以在傳回的instance上立即執行另一個函數,此即鍊式調用。

看下面這段代碼。