This page looks plain and unstyled because you're using a non-standard compliant browser. To see it in its best form, please visit upgrade to a browser that supports web standards. It's free and painless.

Fillano's Learning Notes 會員登入 會員註冊

如果有設定好NODE_PATH系統環境變數,並且在PATH環境變數加上node.exe所在的目錄,那就很方便,因為node.exe會從這個系統變數所指的目錄去尋找模組。

從npmjs.org下載nodeunit並解壓縮後,把package目錄改成nodeunit,並且複製到NODE_PATH所指的目錄中,另外在node.exe所在的目錄中加上簡單的nodeunit.cmd:

node %NODE_PATH%\nodeunit\bin\nodeunit %*

這樣就可以直接呼叫nodeunit來執行測試。

另外,由於nodeunit使用utf8符號以及顏色控制碼,這些在windows console中都會影響測試結果顯示。可以透過修改%NODE_PATH%\nodeunit\bin\nodeunit.json檔案,把顏色控制碼刪除,然後利用chcp指令,把code page改為65001,這樣就能執行並且看到結果了。


2011/10/23 補充:

從node-v0.5.8以後,npm可以直接支援windows,另外,在之前的版本也已經支援在console做有顏色的輸出與utf8符號等,所以已經不需要上面這些步驟了。

但是如果使用node-v0.5.10以上必須注意,這個版本以及之後的版本會把process.ARGV移除,所以跑nodeunit可能會碰到問題(我寫的時候還沒改,但是已經有人根據api change文件提出issue了)。如果執行時碰到問題,建議去修改一下:模組目錄/node_unit/bin/nodeunit,把裡面用到的process.ARGV改成process.argv。

熟悉Javascript運作原理的人,應該都知道Javascript的非同步執行,是採用一個單一執行緒的方式,把所有事件放在一個queue裡面依序執行。John Resig的文章:How JavaScript Timers Work,裡面對於這樣的運作方式有相當清楚的解釋。

在John Resig舉的例子中,執行的單位看起來都是一個一個事件處理函數...不過實際運作應該不只是這樣。ECMA-262標準中,對於Javascript的執行,定義了三種Execution Context,分別是global, eval以及function。而像setTimeout/setInterval,第一個參數可以是字串或函數,所以排進event loop中執行的程式就有可能是函數或字串。更進一步來看,我們可以利用在文件head元素動態附加一個script元素來載入Javascript,這個時候程式就會跑在global context。

現在好奇的一點是,如果這三種execution context的code都可以排入event loop,那麼是否event loop裡面的執行單位其實就是execution context呢?來寫個簡單的測試看:

 (閱讀全文)

從node-v0.3.7之後,nodejs支援在MinGW/MSYS環境中編譯:Building node.js on mingw。不過,如果已經安裝了Visual Studio / VC++,會導致V8誤認編譯器,因而出現編譯問題。

我自己有安裝Visual Studio Express,直接用標準的方式編譯同樣會出問題。不過...直接進入deps/v8,透過scons來編譯v8卻非常順利Orz。觀察了一下錯誤訊息,node在編譯v8時,使用的SConstruct檔案似乎不是v8內建的?稍微改一下wscript,在v8的編譯參數加上toolchain=gcc,好像也沒用,直接使用scons也有問題...不過,嘿嘿,把個別編譯好的libv8.a放到build/default裡面就可以編譯過去了。

不過,openssl還是不行,一定要configure --without-ssl...其實在msys底下有安裝openssl阿...這蠻奇怪,調了wscript也不能過XDDD

今天看到宗董介紹,回頭再看了一下mod_bw...這個module有點舊了,最新版本是0.7,而且更新日期是在2005年...,不果看起來他支援Apache2.0的模組,所以跟Apache2.2應該相容性還OK。

網站上有已經編譯好的模組可用,但是下載來用時發現,AppServ裡面的httpd無法啟動,看起來是載入這個模組出了問題,只好自己編譯看看了

試了一下,比較快的方式是

  1. 進入httpd-2.2.8(之前編譯過,所以用這個)原始碼目錄中的modules/filters目錄
  2. 把mod_bw原始碼中的mod_bw.c複製到這個目錄
  3. 這篇文章的方式修改mod_bw.c的原始碼
  4. 把mod_filter.mak複製成mod_bw.mak,然後把檔案中的"mod_filter"字串取代成"mod_bw"字串
  5. 進入Microsoft Windows SDK 6.1的console,進入上述目錄,然後用"nmake /f mod_bw.mak"命令進行編譯
  6. 然後把編譯好的mod_bw.so拷貝到httpd模組目錄中,然後做好設定,重啟apache httpd

因為我都用Windows SDK + VC Express來編譯,這樣用apxs反而會產生不正確的設定XD,所以修改Makefile反而比較快...

簡單的筆記,怕自己忘記。至於為啥要編譯這個版本...是因為自己目前用的AppServ是用這個版本。比較新的httpd在windows中編譯應該不會碰到這個問題(ex. 2.2.17)。

關鍵在於這個issue,只要用bugzilla連結中提供的patch來修改apr/include/apr.hw還有apr/network_io/unix/multicast.c就可以通過編譯。(我用的是Windows SDK 6.1,一樣會碰到問題)

之前在nodejs討論群回覆別人問題,使用了之前文章:用函數處理函數,來擴充函數原本的功能中,讓函數執行特定次數的方法,配合遞迴來解決問題。不過他的問題,其實把直接執行的程式改成函數就可以解了。

我比較好奇的是效能,所以把這兩種寫法拿來測試一下:

<html>
<body>
</body>
</html>
<script>
//global variables... easier for testing
var input = [],output=[];
var input1=[],output1=[];
//limit a function to run n times. will always return null while over the limit
function times(n, f) {
  var c=0;
  return function() {
    var args = [];
    for(var i=0,j=arguments.length; i<j; i++) {
      args.push(arguments[i]);
    }
    if(c<n) {
      c++;
      return f.apply(this, args);
    } else {
      return null;
    }
  };
}
//the core function for this test.
var work = function(res) {
  return res.shift();
};
//limit the 'work' function to run 10 times
var f1 = times(10, work);
//run recursively for test1
var r1 = function(res, f) {
  var ret = f(res);
  if(null !== ret) {
    output.push(ret);
    r1(res, f);
  }
};
//run iteratively n times for test2
var r2 = function(n) {
  for(var i=0; i<n; i++) {
    output1.push(work(input1));
  }
};
//test1: recursive version, initial global input and output variables then run r1
//the codes within test1 and test2 are almost identical
function test1() {
  input = [0,1,2,3,4,5,6,7,8,9];
  output = [];
  r1(input, f1);
}
//test2: iterative version, initial global input1 and output1 vairables then run r2
function test2() {
  input1 = [0,1,2,3,4,5,6,7,8,9];
  output1 = [];
  r2(10);
}
//run each test 100000 times to make the difference of the two tests visible
var count = 100000;
var d1 = new Date().getTime();
for(var i=0; i<count; i++) {
  test1();
}
var d2 = (new Date().getTime()) - d1;
var d3 = new Date().getTime();
for(var i=0; i<count; i++) {
  test2();
}
var d4 = (new Date().getTime()) - d3;
alert(d2);
alert(d4)
</script>

疑?結果用functional風格的寫法,速度竟然比簡單loop的寫法快了三倍左右(在Chrome8。在Opera差距更大,大約五到六倍,其他瀏覽器跟Chrome8差不多。)...我以為用loop會比較快說

嗯,所以...使用Javascript時,適當地搭配functional、recursive的寫法,並不會影響到效能,有時還會更快!


2010-12-10 19:47 補充:

最近跟噗友討論了一下這個問題:http://www.plurk.com/p/9gb24a,不過還是有點難確定為什麼速度的差距是這樣。

之前參加IT邦鐵人賽,在一個沒做出來的例子中,發現某些瀏覽器對從Canvas 2D Context用getImageData取得的CanvasPixelArray做JSON.stringify/JSON.parse速度奇慢,當時只是粗略的測試。為了仔細觀察到底不同瀏覽器的JSON處理效率,我索性做兩個資料來做比對,一個是640x480的CanvasPixelArray,一個是相同結構的Object。

CanvasPixelArray有兩個重要的使用方式,其中,使用length性可以取得他的「陣列」(他不是一個真正的陣列)長度,用數字當作索引,可以取得依照RGBA順序排列的值。所以一個640x480的Canvas,取得的CanvasPixelArray元素個數應該有640x480x4。跟CanvasPixelArray做對照的是一個JSON.stringify後,結果相同的Object。然後測試兩個樣本執行JSON.stringify以及JSON.parse的執行速度。程式如下:

<html>
<head>
<style>
  div {
    border: solid 1px #336699;
    background: #AADDFF;
    width: 800px;
    padding: 10px;
    margin: 5px;
    border-radius: 5px;
    -moz-border-radius: 5px;
    text-align: justify;
  }
</style>
</head>
<body>
<canvas id="canvas" width="640" height="480"></canvas>
<div id="panel"></div>
</body>
</html>
<script>
document.getElementById('panel').innerHTML += 'Preparing CanvasPixelArray for testing...<br>';
var ctx = document.getElementById('canvas').getContext('2d');
var img = ctx.getImageData(0,0,640,480);
document.getElementById('panel').innerHTML += 'Preparing Object for testing...<br>';
var obj = {};
for(var i=0,j=640*480*4; i<j; i++) {
  obj[''+i+''] = 0;
}
obj['length'] = 640*480;
function test1() {
  var time1 = new Date().getTime();
  txt = JSON.stringify(img.data);
  var time1a = new Date().getTime();
  document.getElementById('panel').innerHTML += 'JSON.stringify CanvasPixelArray: ' + (time1a-time1)/1000 + 'secs <br>';
  setTimeout(test2, 100);
}
function test2() {
  var time2 = new Date().getTime();
  var tmp = JSON.parse(txt);
  var time2a = new Date().getTime();
  document.getElementById('panel').innerHTML += 'JSON.parse CanvasPixelArray: ' + (time2a-time2)/1000 + 'secs <br>';
}
setTimeout(test1, 100);
function test3() {
  var time1 = new Date().getTime();
  txt1 = JSON.stringify(obj);
  var time1a = new Date().getTime();
  document.getElementById('panel').innerHTML += 'JSON.stringify Object: ' + (time1a-time1)/1000 + 'secs <br>';
  setTimeout(test4, 100);
}
function test4() {
  var time2 = new Date().getTime();
  var tmp = JSON.parse(txt1);
  var time2a = new Date().getTime();
  document.getElementById('panel').innerHTML += 'JSON.parse Object: ' + (time2a-time2)/1000 + 'secs <br>';
}
setTimeout(test3, 3000);
</script>

測試的結果還是讓我蠻訝異的:

  • Google Chrome 8(8.0.552.200 beta)
    • CanvasPixelArray
      • JSON.stringify:5.184 seconds
      • JSON.parse:12.273 seconds
    • Object
      • JSON.stringify:4.639 seconds
      • JSON.parse:9.787 seconds
  • Firefox 4 Beta7
    • CanvasPixelArray
      • JSON.stringify:2.901 seconds
      • JSON.parse:3.522 seconds
    • Object
      • JSON.stringify:3.628 seconds
      • JSON.parse:3.484 seconds
  • Firefox 3.6.12
    • CanvasPixelArray
      • JSON.stringify:0.694 seconds
      • JSON.parse:1.134 seconds
    • Object
      • JSON.stringify:3.683 seconds
      • JSON.parse:3.552 seconds
  • Opera 10.63
    • CanvasPixelArray
      • JSON.stringify:4.029 seconds
      • JSON.parse:0.639 seconds
    • Object
      • JSON.stringify:3.984 seconds
      • JSON.parse:0.638 seconds

雖然只是很粗略的測試,而且只跑一次(所以不夠格當作樣本),但是一些速度的差距太明顯,所以很有意思。Google Chrome雖然速度很快,但是他的JSON處理速度有點慘不忍睹(Safari也一樣,但是Safari會警告Javascript執行逾時,測試根本跑不完)。Opera的JSON.parse速度讓人驚艷。Firefox4 Beta7的速度反而不及Firefox 3.6.12。而Firefox 3.6.12處理CanvasPixelArray的速度很明顯地比Object快,這也跟預期不太一樣。

這只是個簡單的筆記。nodejs好幾個版本前就支援在cygwin環境中編譯,但是我編譯一直會出錯。出錯時看起來都是在跑python,所以懷疑是python的問題。

今天把cygwin中的python版本從2.6降到2.5,下載nodejs v0.2.4下來編譯,發現一切順利。看起來果然就是python版本的問題。


2010-10-27 8:20 補充

node-v0.3.0 unstable的編譯會出問題,不過在這個討論串,Raffaele Sena 提到一個解法,就是在deps/libev/cygwin/config.h中加入一行:

#include "/usr/include/cygwin/config.h"
嗯...看起來是因為目錄安排,使得引入cygwin/config.h出問題...

除了跨應用軟體間的drag&drop,另外一個可以測試的是同一瀏覽器但是不同視窗不同文件之間的drag&drop。其實使用的方式跟前面兩篇文章:HTML5 Drag&Drop + 純AJAX檔案上傳以及一個簡單的HTML5 Drag & Drop試作差不多。目前測試到比較可用的方式,是過濾Event.dataTransfer.types,看看裡面是否有一筆資料為'text/uri-list',然後用Event.dataTransfer.getData('text/uri-list')就可以取得拖拉檔案的網址,接下來就可以使用這個資料來做一些處理。

如果要讓網頁可以同時處理其他應用軟體與跨文件drag&drop,我想到比較好的方式是先透過Event.dataTransfer.types裡面是否有'Files'字串來判斷(DataTransfer.types)。但是這時發現Firefox4 Beta6有一個問題...Orz。在從另一個Firefox視窗拖曳圖檔進來時,並沒有在Event.dataTransfer.files設定檔案資料,但是Event.dataTransfer.types卻有一筆資料是'Files',這樣會造成原本的程式邏輯有問題。所以只好額外用Event.dataTransfer.files.length來判斷是否有檔案資料。

利用這樣的判斷方式,就可以在有圖檔的File物件收到時,上傳檔案並且顯示上傳後的檔案。如果收到的資料是圖檔的url,就利用這個url來顯示圖檔。以下是從前兩篇文章再做修改的測試:

 (閱讀全文)

(有一些基本說明在前一篇文章:「一個簡單的HTML5 Drag & Drop試作」裡面提過了,這裡就不在重複了。)

除了在頁面之內使用拖拉,HTML5更強的地方,在與其他應用軟體間也能拖來拉去!這主要是靠DataTransfer.files屬性達成的。DataTransfer.files其實是一個FileList物件,而FileList則是包含了File物件的List...(廢話),這幾個東西,其實不是定義在HTML5裡面,而屬於File API。

當從其他應用軟體或作業系統拖曳檔案進來時,dataTransfer.types裡面會有一個值'Files',這時相對地在dataTransfer.files裡面就會有File物件,裡面有拖曳的檔案資料。要讀取File物件,則需透過FileReader物件,這些都是屬於File API裡面定義的物件。先來看看這些物件的定義及特性:

 (閱讀全文)

上週五跑去Google DevFest2010,聽到Kurrik提到,HTML5的Drag & Drop,還可以支援跨html文件及跨應用軟體。最近在研究HTML5相關的東西,有看到規格書第七章User Interaction裡面有一節在講Drag and Drop,但是沒有仔細看。聽過Kurrik的介紹以後,興趣來了,所以就先來試試。

過去要做出拖曳,需要用很複雜的方式偵測滑鼠位置來移動拖曳物件,HTML5把這個過程簡化了一些。Drag & Drop是靠幾個事件、物件跟HTMLElement屬性來協作的:

  1. DataTransfer 物件:透過Event.dataTransfer參數傳進Handler Function,這是在物件之間傳遞訊息的媒介。
  2. draggable 屬性:有設定這個屬性的物件才能被拖曳,否則可能會發生文字選擇的事件而不是拖曳的事件。
  3. ondragstart 事件:在被拖曳物件開始拖曳時觸發,這個事件作用在被拖曳物件
  4. ondragenter 事件:在被拖曳物件進入目的物件時觸發,這個事件作用在目標物件
  5. ondragover 事件:在被拖曳物件在目的物件上移動時觸發,這個事件作用在目標物件
  6. ondrop 事件:在被拖曳物件位於目的物件上放開滑鼠按鈕時觸發,這個事件作用在目標物件
  7. ondragend 事件:在ondrop之後觸發,這個事件作用在被拖曳物件
  8. Event.preventDefault() 方法:必須適時執行這個方法,來避免預設的動作被執行。在ondragover中一定要執行preventDefault(),ondrop事件才會被觸發。另外,從其他應用軟體或是文件拖曳東西進來,尤其是圖片時,預設的動作是顯示這個圖片或檔案,而不是真的執行drop。這個時候要用document的ondragover事件把他擋掉。
  9. Event.effectAllowed 屬性:指定要顯示的拖曳動態效果。

DataTransfer物件還是需要稍微介紹一下。他的定義可見HTML5標準文件的:7.9.2 The DragEvent and DataTransfer interfaces,其中對於DragEvent定義如下:

 (閱讀全文)

最近開始想要用自己寫程式的方式,透過規格文件來學習HTML5。不過看過規格,發現許多API還是繼承自DOM規格,所以還是需要花時間回頭看一下DOM3的相關文件。

記得在John Resig及PPK的Blog或是書裡面,有看過一些對於瀏覽器相容性的建議,主要是關於不要用瀏覽器來決定是否支援某項功能,而應該直接偵測某項功能在某個瀏覽器是否可以使用。基於這樣的想法,之前也寫了個小程式來偵測瀏覽器對於html5 tag的支援。其實想法很簡單,只是從標準文件上找到某個物件具備的properties,然後測試這些東西實際在瀏覽器中是否存在。

從ECMA-262 Edition3裡面物件的properties就有[DontEnum]屬性,如果有這個屬性,使用for...in就無法列舉出來。不過只是不會列舉,並不是無法使用。之前偵測IE的window物件就有碰到這樣的問題,IE8把許多東西都設為[DontEnum],所以用for..in來偵測的話,很多功能就跑不出來。這樣,就得想辦法自己列舉該有的properties,檢查他是否在物件中為undefined。

HTML5中,所有的元素都是HTMLElement,這個Interface繼承了DOM的Element,而Element又繼承了DOM的Node Interface。同樣,在其他規格中也與DOM有關,例如Drag & Drop中,事件會繼承DOM3 Event的核心Event Interface,有這些線索,就可以根據規格一路追過來。因為要處理許多Interface,在寫程式的過程中就歸納了一些自己會用到的函數:

function row(cells) {
  return '<tr>' + cells.join('') + '</tr>\n';
}
function cell(text, style, col) {
  return '<td' + (col? ' colspan="'+col+'"':'') + (style? ' style="'+style+'"':'') + '>' + text + '</td>';
}
function DOMInspector(title, obj, attr) {
  var ret = {
    'title':title,
    'SupportedAttributes':[],
    'UnsupportAttributes':[],
    'SupportedMethods':[],
    'UnsupportMethods':[]
  };
  for(var i=0; i<attr.attr.length; i++) {
    if(obj[attr.attr[i]] !== undefined) {
      ret.SupportedAttributes.push(attr.attr[i]);
    } else {
      ret.UnsupportAttributes.push(attr.attr[i]);
    }
  }
  for(var i=0; i<attr.mthd.length; i++) {
    if(obj[attr.mthd[i]] !== undefined) {
      ret.SupportedMethods.push(attr.mthd[i]);
    } else {
      ret.UnsupportMethods.push(attr.mthd[i]);
    }
  }
  return ret;
}
function resultParser(test) {
  var str = '<table cellpadding="2" cellspacing="0" border="1">';
  for(var i in test) {
    if(i == 'title') {
      str += row([cell(test[i],null,2)]);
    } else {
      str += row([cell(i),cell(test[i].length>0? test[i].join('<br>'):' ')]);
    }
  }
  str += '</table>';
  return str;
}
有了這幾個函數,我在需要偵測瀏覽器對於DOM3 Core中定義的Node Interface時,只要針對這個interface的規格寫好一個property的對應就可以了:

 (閱讀全文)

為了要學習html5新規格,一個方法就是利用寫程式的機會來熟悉,所以就想寫個小程式來偵測html5規格中定義的tag。 最早的想法,是用document.createElement(),傳入規格書裡面規定的tagName,看看產生物件會不會出問題,但結果是...一律不會出問題 (汗)。(如果是xml,我用任何自訂的tag都不應該有問題,是我太天真)所以看起來還需要加不少工。更可靠的方式,是判斷產生出來的物件,是否有該有的property。這個可以用這些property是否為undefined來判斷,需要的話,還可以賦值,看看會不會產生錯誤。依照這個想法,寫了一個簡單的程式來判斷瀏覽器對於html5規格中定義的tag,支援如何:(放在首頁太長了,所以請進入觀看 :-D)

 (閱讀全文)

昨天突然有一個想法,就是也許我們可以用WebWorker做壓力測試,這樣只要把簡單的網頁程式放在網站中,下一些條件來產生壓力。

花了一點時間寫了一個小程式來做了簡單的測試,包含網頁及webworker部份。網頁部份如下:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="js/jquery-1.4.2.min.js"></script>
<script>
$(document).ready(function(){
  var workers = [];
  var count = 0;
  var start = 0;
  $("form#myform").submit(function(){
    var url = $("input#uri").val();
    var num = $("input#concurrent").val();
    var start = $("input#start").val();
    var times = $("input#times").val();
    function workercb(e) {
      $("#panel")[0].innerHTML += e.data + ":" + Math.round(((new Date().getTime())-start)/1000) + "<br>";
      if(count<times) {
        this.postMessage(JSON.stringify({'cmd':'action','count':count}));
        count++;
      }
    }
    for(var i=0; i<num; i++) {
      var tmp = new Worker('test602.js');
      tmp.onmessage = workercb;
      workers.push(tmp);
    }
    for(var i=0; i<workers.length; i++) {
      workers[i].postMessage(JSON.stringify({'id':i,'cmd':'prepare','uri':url}));
    }
    start = new Date().getTime();
    for(var i=0; i<workers.length; i++) {
      workers[i].postMessage(JSON.stringify({'cmd':'action','count':count}));
      count++;
    }
    return false;
  });
});
</script>
</head>
<body>
<form id="myform">
<table padding="2" spacing="0" border="0">
<tr><td><label for="uri">下載資源URI:</label></td><td><input type="text" name="uri" id="uri" size="30"></td></tr>
<tr><td><label for="concurrent">Worker數量:</label></td><td><input type="text" name="concurrent" id="concurrent" size="5"></td></tr>
<tr><td><label for="start">發動時間:</label></td><td><input type="text" name="start" id="start" size="10"></td></tr>
<tr><td><label for="times">發動次數:</label></td><td><input type="text" name="times" id="times" size="10"></td></tr>
<tr><td colspan="2"><input type="submit"></td></tr>
</table>
</form>
<div id="panel"></div>
</body>
</html>
webworker程式如下(test602.js):
var config = {
  'id': '',
  'uri': '',
  'start': null,
  'end': null,
  'count': 0,
  'result': 'fail'
};
var count = 0;
onmessage = function(e) {
  var payload = JSON.parse(e.data);
  switch(payload.cmd) {
    case 'prepare':
      config.uri = payload.uri;
      config.id = payload.id;
      break;
    case 'action':
      config.count = payload.count;
      action();
      break;
  }
};
function action() {
  var req = new XMLHttpRequest();
  var parm = new Date().getTime() + '' + Math.round(Math.random()*100000) + '' + count;
  req.open('GET', config.uri+'?'+parm, false);
  config.start = new Date().getTime();
  req.send(null);
  config.end = new Date().getTime();
  if(req.status==200) config.result = 'success';
  else config.result = 'fail';
  postMessage(JSON.stringify(config));
  count++;
}

 (閱讀全文)

今天又想到,在Javascript中可以用函數來處理一群函數,建立之間的關係,組織起來使用會比較方便。

循序、間隔執行數個函數應該是蠻簡單的,比較複雜的是嵌套,例如 a(b(c())) 這樣的形式。要怎麼建立這樣的執行關係呢?今天花了一點時間想出怎麼做:

function Compositor(){
  var args = [];
  for (var i=0; i<arguments.length; i++) {
    args.push(arguments[i]);
  }
  return function(n) {//這樣會做出一個currying,來接收要處理的參數n
    var req = function(x) {
      if(args.length>0){
//如果不是最後一個函數,就遞迴下去
        return x(req(args.shift()));
      } else {
//如果是最後一個函數,就把要處理的參數n傳給他執行,結束遞迴
        return x(n);
      }
    };
    return req(args.shift());
  };
}
function a(n){return --n;};
function b(n){return n+3;};
function c(n){return n*n;};
var d = Compositor(a, b, a, c);
alert(d(4));// 結果是17
alert(a(b(a(c(4)))));// 對照組,執行的結果應該要跟這個相同,都是17

測試的結果都是17。要執行的函數依序傳入,由外而內形成類似「嵌套」的關係,最後一個函數會先執行,執行結果當作參數傳給外層的函數,外層函數執行結果再傳給更外層的函數當作參數等等,循環下去。這個結構看起來也有點像decorator,其實也能拿來這樣用。

有時間再來想想還有怎樣的「關係」可以把函數組合起來使用。


2010-9-25 22:33 補充:

一直在想這樣的函數關係叫做什麼,剛剛找了一下,在wikipedia發現這個:

看起來就是我想做的東西。chain也叫做fluent interface,通常會指另一種東西,還是改掉比較好。所以我把程式中的函數名稱從Chain改成Compositor。

1 2 3 4 5 6 7 8 9 10  下一篇»