20K for...of

for...of是 ECMAScript 2016 的新語法,有了他之後,要用迴圈跑過陣列不用像以前一樣先用for...in或是用傳統的取長度,然後i++的方法:

var arr = [1, 2, 3];
var i, v, len;

for (i in arr) {
    v = arr[i];
    console.log(v);
}

for (i = 0, len = arr.length; i < len; i++) {
    v = arr[i];
    console.log(v);
}

現在只要用簡單的for...of就可以了:

var arr = [1, 2, 3];

for (let v of arr) {
    console.log(v);
}

不過目前還是需要考慮只有 ECMAScript 5 的環境,例如 IE11,所以一般都還是會用像是Babel之類的 transpiler 來把 ES2015 的 syntax 轉成 ES5 的 code,結果轉出來如下:

"use strict";

var arr = [1, 2, 3];

var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
    for (var _iterator = arr[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
        var v = _step.value;

        console.log(v);
    }
} catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
} finally {
    try {
        if (!_iteratorNormalCompletion && _iterator.return) {
            _iterator.return();
        }
    } finally {
        if (_didIteratorError) {
            throw _iteratorError;
        }
    }
}

結果其實有點意外,一個簡單的for...of竟然變的這麼長,事實上是因為for...of其實沒想像中簡單,因為它可以用的地方其實不只是陣列,而是iterable 物件,不過為了要完整的支援for...of,就變成需要有 iterator, generator, symbol 等等的支援,當然上面的程式碼不能在 ES5 環境下執行,而 Babel 依靠的是babel-polyfill,裡面其實就是core-jsregenerator,不過這一整包,其實有點龐大,要 228KB,即使最小化之後也還要 95KB,所以,就想著是不是能夠只捆包進需要的部分就好了,研究過後,發現有 Babel plugin 叫做transform-runtime,套用上去後:

import _getIterator from "babel-runtime/core-js/get-iterator";
var arr = [1, 2, 3];

var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
  for (var _iterator = _getIterator(arr), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
    var v = _step.value;

    console.log(v);
  }
} catch (err) {
  _didIteratorError = true;
  _iteratorError = err;
} finally {
  try {
    if (!_iteratorNormalCompletion && _iterator.return) {
      _iterator.return();
    }
  } finally {
    if (_didIteratorError) {
      throw _iteratorError;
    }
  }
}

可以看到原來用Symbol取 iterator 的地方變成用_getIterator了,而且還有一行:

import _getIterator from "babel-runtime/core-js/get-iterator";

如果要真的把這部分也打包進來,則需要讓 bundler 處理,我個人是偏好rollup,搭配以下兩個 plugin:

然後用以下的設定:

babel({
  exclude: 'node_modules/**',
  plugins: ['transform-runtime'],
  presets: ['es2015-loose-rollup'],
  runtimeHelpers: true
}),
nodeResolve({ jsnext: true }),
commonjs({
  include: 'node_modules/**'
})

結果,就可以得到夢寐以求的 20KB 的程式碼了,當然 20KB 的部分不是預期的啦,相較於一開始的程式碼只有 72Bytes,為了一個for...of變成 20KB 好像有點本末倒置,畢竟我只有要在 Array 上用,難道不能只是簡單的轉成for...in型式嗎。

事實上是有辦法的,第一個就是改寫TypeScript,TypeScript 對於for...of只有兩種處理方法,而且結果都不會如此膨脹,第一種就是變成for...in,第二種則是不變動,保留for...of的語法,後者是在 target 設定成 ES6 的時候使用的,官網也有相關的說明

第二種方法則是用Bublé取代 Babel 做為 transpiler,Bublé 是 rollup 的作者Rich Harris的另外一個作品,我個人是蠻喜歡他的哲學的,Bublé 的哲學則是對於 code 做簡單、直接明瞭的轉換,所以for...of就只會轉成for...in的型式,不過也因此無法支援 iterable 物件,所以預設是不開啟支援的,歸類在dangerious transofrm 之下,另外 Bublé 也還不支援 Async/Await,因為要做出支援 ES3/5 的同樣效果的 code 會增加太多的複雜度,不符合他的哲學理念,所以目前還沒有計畫支援,這點倒是 TypeScript 支援比較完整,目前的2.1 RC已經支援把 Async/Await 轉成 ES3/5 的版本了。

最後結論,基本上就是個取捨,Babel、TypeScript、Bublé 各自有它們的優缺點,所以只能看情況選擇了,如果要 Map/Set 也要在這些物件上用for...of語法然後也要 Async/Await,那就只能用 Babel 加上 babel-polyfill;如果可以不要 Map, Set 或是可以接受不在這些物件上使用for...of語法(還可以用 forEach),那可以選擇 TypeScript,然後加上 Map/Set 的 polyfill,如果不用 Async/Await,也不用 Map/Set 的話,可以考慮用個 Bublé 看看。不過如果完全不需要考慮 ES3/5 的環境的話(Edge, Firefox, Chrome 都已經對 ES2015 支援很完整了),好像問題突然就小很多了XD,最後附上這篇文章提到的各種作法產生的檔案參考,目前都放在 github 上的20k-for-of這個專案。


Vim Filename Complete

Vim Filename Complete,

Vim 有一個內建的自動補完功能是針對檔案名稱的,使用的方法是<C-X><C-F>,我目前在維護的autocomplpop也有支援這種補完模式,只要輸入./後就會自動幫忙觸發,不過我比較有機會觸發到是在使用 ECMAScript 6 的 import 和 CSS 的 import 時,不過常常就是發現他查看的路徑不太對,不是拿目前編輯檔案的位置做為起點的,研究過後發現是因為 Vim 找檔案的起點是看他的工作目錄($PWD),加上我會使用ctrlp這種工具,所以實際上在編輯的檔案通常是不在工作目錄下,對於這個問題,其實我覺得最理想的解決方式是 Vim 應該要提供兩種模式來決定要從那邊開始找,不過目前似乎沒這個計畫,唯一在文件是有提到的是未來可能會支援path的設定,理論上,如果有支援的話,應該就可以解決問題了,因為預設的path值包括了.,不過目前還沒有相關時程,就只能自救了。

最簡單的方法,其實就是開啟autochdir,這個選項打開後就會自動在切換 window 時也更改工作目錄,不過這個選項是為了相容早期系統才提供的,文件也有說可能會和部分 Vim Script 不相容,實際上我也有找到一些不相容的 Vim Script,所以想避免,就搜尋了一下其它可能的解決方法,在 StackOverflow 上有看到一篇,裡面有兩個人提供了解法,第一個是用autocmd,然後在進入 insert mode (在這時候才有機會用到檔名補完的功能)時自動開啟autochdir,離開時自動關閉autochdir,不過這樣的方式(感覺上)還是不太安全,因為還是用到autochdir,所以下面有另外一個方法改用lcd,作法是改成修改 Key Mapping 的方式,改的 mapping 是./<C-X><C-F>,不過這樣對我來說又不合用,因為我用 autocomplpop 的話,不會真的打<C-X><C-F>,所以基本上觸發不到這事件,所以我就決定把這兩種解法合併起來,改成用autocmd加上lcd

:autocmd InsertEnter * let save_cwd = getcwd() | execute 'lcd %:p:h'
:autocmd InsertLeave * execute 'lcd' fnameescape(save_cwd)

進入 insert mode 時改變該 window 的工作目錄,離開 insert mode 時把工作目錄還原。這是我目前認為影響最小的調整方式,不過其實可能執行一次lcd換工作目錄就夠了,沒深入研究 autochdir 所產生的問題,不過我推測是影響到 Vim Script 建立的 window 的工作目錄,像是 NERD Tree 之類的側邊欄那種,總之目前這樣運作還算正常,接下來就是等 Vim 加上path的支援吧(或是有人送 patch)。


SBB Mobile

SBB Mobile,

之前去瑞士的時候,主要的交通工具是靠火車,所以就下載了瑞士國鐵的 Android App:SBB Mobile,用過後覺得真是很棒,首先上面的畫面就是一開始進去看到的,很直接了當的就是最重要的功能,查詢路線和時間,畫面很乾淨,就是起點、目的地、出發或到達時間,還可以加上中間站,而且時間會自動更新到現在時間,然後隨便查一下就可以看到下面的畫面:

閱讀「SBB Mobile」全文

Acclerated Mobile Pages

O2 DAC + AMP,

Acclerated Mobile Pages簡稱 AMP,是 Google 所推出,為了提升行動網路體驗的一個專案,我一開始對於這種(看似)偏離網路標準的方案其實不太有興趣,不過在瞭解其技術原理後,覺得相當有趣,而且其實沒有想像中的偏離標準,整個架構也比 Facebook 的 Instant Article 還要來的開放,雖然我覺得這個解決方案(只說是 Framework 或是 Library 都不夠完整),其實只算是個暫時的解法,但是 AMP 本身的實做方式,其實是架構在一堆網路標準的發展之上的,相當出人意料,可以說是Extensible Web 宣言以來,第一個重要的里程碑。

閱讀「Acclerated Mobile Pages」全文

Native True Color Vim

因為最近 Vim 8 發佈了,所以就又研究一下現在最新的 True Color Vim 安裝方法,結果發現已經併進 master branch 許久了,然後從7.4.1784開始,也不用加特別參數來編譯,只要--with-features的值是big或是更大的huge就會把這功能編譯進去,所以現在就不用 ZyX 維護的版本了,目前用的編譯指令為:

git clone https://github.com/vim/vim.git

cd vim
cd src && make autoconf && cd ..

./configure \
  --enable-gui=no \
  --without-x \
  --enable-multibyte \
  --with-tlib=ncurses \
  --enable-cscope \
  --with-features=huge \
  --disable-nls \
  --enable-perlinterp \
  --enable-pythoninterp \
  --enable-rubyinterp

make
make install

然後現在也不需要guicolors的設定,好像直接就生效了,顏色畫出來和之前的 ZyX 的版本似乎有一點差異,我想應該現在新的版本是比較正確才是。追蹤這功能追了這麼久,總算也是告一段落了,感覺...好像也沒什麼特別的感覺...


➡ 看看其它文章