markdown 中簡單使用 js 巨集

之前加了一個像巨集的功能,能用比較簡短的語法寫 js 巨集。 畢竟直接用 <script> 太麻煩,寫起來也太長不方便。 可以用 js ejp 的方式嵌入 expression, 在瀏覽器上 eval 出結果。

因為 js 彈性太大,所以定了一些慣例。 例如我可以瀏覽時才用 regexp 抓出語法然後代換, 也可以手寫 html,然後用 dom 介面抓出 html 代換。 我決定盡量使用後者,畢竟 dom 已經很成熟了, 而且既然已經是 html,也應該多用 tag,不要用其它語法。

但手寫 html 還是太麻煩, 我可能得挑一個幾乎沒在用的標籤, 然後在 js 中 eval 她。 且 html 標籤是出名的字多,就算只有一個字的標籤, 開啟加閉合也要 7 個字 <i></i>

自定義 markdown 語法

我決定是要在 markdown converter 上做手腳, 把選定的語法轉成 <code class="javascript-eval"> 之類的。 然後就能用 $('code.javascript-eval') 簡單抓出來。

語法目前是決定用 js ejp 因為我使用的可能會用到跳脫字元, 那 markdown 原本的 code 語法就能幫跳脫。

另一個原因是目前我還沒對 converter 開刀,感覺就很麻煩。 js ejp 這種語法,如果什麼都不做,也會變成一個 code 元素, 可以簡單用 document.querySelector('code').filter((e) => /^js /.test(e.textContent)) 抓出來,寫起來很方便。

this node

我本來想做一個功能,讓 js expression 在被求值時, 能存取自己所在的那個 node。 但一直想不到做法, 因為就算在 eval 中的 this 是指向該 node, 但 eval 中的函數呼叫的 this 還是會指向 window。 (沒有指定 caller 的 function call,caller 都是 window。)

而且因為 js 是 lexical scope, 函數中的自由變數都在定義時決定了, 也就是不管函數在哪裡執行,結果都一樣。 (大部份啦。)我有點懷疑 lexical scope 甚至 functional programing 與 meta programing 衝突

有一種做法是把該 node 透過該函數名傳進去, 像 foo.theNode = node ; foo('abc', 12) , 然後寫巨集函數時如果要用 node 就存取 theNode 屬性。 或是直接當最後一個參數或用 foo.call(node) 指定。 但這幾種做法都要手動 parse 語法, 第一種要抓出函數名稱,後二種更要抓出傳了哪些參數,更麻煩。

最後是發現多種返回值如果返回高階函數好像可以做到。

多種返回值

eval 是直接用 eval 函數,在 window.onload 執行。 但我想過到底要返回什麼,後來決定依返回什麼, 做不同的處理。

像 jQuery 的做法,如果傳 string 或 html 就把內容換成該 html 片段。 傳 html node 就 append 上去, 傳 function 就把原本的內容當 function 的參數或 this, 然後把內容換成函數的返回值。 另外我還加了如果是 promise 就等 promise 返回, 如果是 iterable 就對每個內容做同樣的處理。

如果要改原本的 code node,就讓該巨集函數的 eval 結果返回另一個函數, 函數就會收到 code node 當參數,然後就能改了。