JavaScript 関連のちょっとしたメモ

気になって調べたり、見つけたもの。小ネタ。基本的にはmonjudoh/BeautifulProperties.js · GitHub をみてたってのがきっかけ。

prototype 汚染しちゃだめなのって Object と Array だっけ?

というか prototype 汚染しないほうがいいのってなんでだっけ?という基礎的な話。prototype.js でいろいろ苦しい話がでたからやめようって経緯があるのは知ってて、名前空間分けようってのは知ってたけど、じゃあどこまでならやっていいんだろうみたいな話。

具体的には以下
JavaScriptでのbuilt-in/DOM objectのprototype拡張 - 文殊堂

Object.defineProperty で enumerable: false はしってたけど、なるなるという。

arguments は array じゃないよって話

答えは本に載ってた

JavaScriptテクニックバイブル ~効率的な開発に役立つ150の技

JavaScriptテクニックバイブル ~効率的な開発に役立つ150の技

Array っぽいけどちょっと違う独自のオブジェクトですよという。まあたしかに console.log() した結果で [object Arguments] ってなってたのはみたけど、じゃあなんなの、という状態だった。で、これを配列として扱いたいときは以下のイディオムですよねという。

Array.prototype.slice.apply(this, arguments)

ちなみに上記のイディオムは arguments にたいしてだけじゃなくて Array に変換したいときに一般的にある話で、具体的には DOM で document.getElementsByClassName() とか $(".class") とかしたときにかえってくるのが HTMLCollection とか HTMLElemnt とか NodeList とかそんな感じなので、変換したりする。

カスタムイベントについて

Hit-a-Hintブックマークレット2.0をリリースしたよ - by edvakf in hatena を空間ナビゲーション版やエクステンション版や、あるいは vimOperate という拡張でもやれるけど、いまでもつかっている。で、読んだときに「マウスイベント作って→初期化して→発火」というくだりでクリックイベントをエミュレーションできるのだけど、でも jQuery.trigger ってあれどうやってるの?と疑問に思った。

まずは MDN をあたった
document.createEvent - DOM | MDN

そうだよね、いろいろあるよね。マウスイベントだけじゃなくて DOM 操作や DOM Level 3 Events からはキーボードイベントもとれるよね、いうのはわかった。でもじゃあ自前でイベントをつくるにはどうすればいいんだろう?というのは残った。ら、既にやっている人がいた。

javascriptのカスタムイベントを作ろう! - 馬鹿と天才は紙一重

つまりイベントオブジェクト作ってしまえば名前なんでもよかったのだ。で、リスナーもなんでもよかったのだ。

// リスナーをはる
document.addEventListener("hoge",function () {
  console.log(1);
}, false);

// イベントオブジェクトつくって
var e = document.createEvent("Event")
// 初期化して
e.initEvent("hoge", bubbles, cancelbuble);
// 対象のエレメントに発火
document.dispathEvent(e)
// 1

まあ

小ネタ

いまさら JSDeferred と $.Deferred を少しさわったメモ

コールバック地獄的なそういうあれを垣間見てしまったので、たしかにもうちょっといいかんじに解決したいってのがよくわかった。そこで書き方を変えることでいい感じに . でチェインしたり例外処理をしやすくするために Deferred がある。ので、簡単なメモとかじったサンプルを張る。

あとは以下の記事に触発されたのもある
JavaScriptとコールバック地獄 - Yahoo! JAPAN Tech Blog
JavaScriptと非同期のエラー処理 - Yahoo! JAPAN Tech Blog
爆速でわかるjQuery.Deferred超入門 - Yahoo! JAPAN Tech Blog

同期と非同期

JS にかぎらず、同期と非同期という考え方がある。処理を逐次行なっていって順番に処理していくのが同期的だとすると、いったんスタックやキューにためておいて「あとでやるからちょっとまって」というように遅延させておくのが非同期処理。Python/Django だと普通に書けば同期的になるけど、celery と RabbitMQ あたりをつかったりさせて非同期処理でバッチさせるというようなことがある(まああとはOS側である程度スレッドつかってくれたりもする)。

で、JavaScript において。発端としてブラウザで動くものとしてあるのでシングルスレッドであると。だから重たい処理をさせるとブラウザが固まる。そうなると困るよね、ってことで、setTimeout とか XMLHttpRequest するところあたりでは同期ではなく非同期処理となってる(という理解)。だってブラウザがいろいろ通信してる間に固まったら困るよねっていう。

たとえば同期的な sleep を実装するとこうなる。

function sync_sleep(ms) {
  var start = new Date().getTime();
  var end = start + ms;

  while (true){
    var tmp = new Date().getTime();
    if (end < tmp){
      break;
    }
  }
}

でも長いと固まるしブラウザが中断してくるかもしれない。じゃあ非同期にする。

function async_sleep(ms) {
  setTimeout(function () {
    console.log(1);
    setTimeout(function () {
      console.log(2);
      setTimeout(function () {
        console.log(3);
        setTimeout(function () {
          console.log(4);
        }, ms);
      }, ms);
    }, ms);
  }, ms);
}

で、そうすると素直に書くとコールバック関数がネストして眠くなってくる。実際は xhr や $.ajax のようなことをネストしたくなったり、あとは Node で本格的に書こうとするとだいぶつらぽよくなってくる

Deferred の共通の考え方 - callback による非同期処理を簡潔にする

setTimeout(callback, 0) をいれておくことで擬似的にスタックに詰めておくようなことができる。ハック。ブラウザの UI スレッドを止めない。すごい。具体的な資料は以下。はてなの @cho45 さん。2009 年にはやってたことなんだよなぁ。
cho45/jsdeferred · GitHub
JSDeferredがやっとわかった - by edvakf in hatena
JSDeferredで,面倒な非同期処理とサヨナラ:特集|gihyo.jp … 技術評論社

おぼろげだけどソース読んだりもした。自分の理解としては

  • Deferred オブジェクトを生成して管理
  • Deferred オブジェクトを起点に処理を next や error でチェインしてかける
  • 内部的には next や error ごとに Deferred オブジェクトを生成して管理してやる -> チェインできる

というくらいの理解

JSDeferred をちょっとだけかいた

var dfd = require("/path/to/jsdeferred.js").Deferred;

dfd.next(function () {
  console.log(1);
}).wait(1).next(function () {
  console.log(2);
}).wait(1).next(function () {
  console.log(3);
}).wait(1).next(function () {
  throw "throw!";
  console.log(4);
}).error(function () {
  console.log('error');
}).next(function () {
  console.log(5);
}).next(function () {
  console.log(6);
});

いい感じですね。

JSDeferred と $.Deferred の対比

JSDeferred もあるけど、 jQuery(1.5から)も似たような機能がついた。だいたい似てるけどちょっとまとめる
uu59のメモ | jQuery.Deferredその1: JSDeferredとの基本的な違い
jQuery.Deferredって何 - Takazudo hamalog
jQueryのDeferredオブジェクトについて調べてみた - AOEの日記
ちゃんと検証してないけど、自分なりの理解としてはだいたいこんな感じ

  • JSDeferred の next, error に対して jQuery.Deferred の done, fail
  • JSDeferred の call, fail に対して jQuery.Deferred の resolve, reject
  • jQuery.Deferred ではユーザーに状態を変更されないように promise して Deferred オブジェクトを返す
  • JSDeferred の parallel に対して ちょっとちがうけど jQuery.Deferred では when がある
  • jQuery.Deferred だと then で dfd(resolve, reject) とかける

まああとは実際に使う機会があったらちゃんとやる。

まあ

そもそもにおいて setTimeout(callback, 0) という発想がすごい。あとは使う機会があったらちゃんとやる。この手の問題はだいぶ認知されてるみたいで、async系のライブラリは npm とか探すと出てくる。

Backbone.js をちょっとだけさわった

JS でMVC的に切り分けるということをちょっと学びたかったのですこし触った。具体的に触発されたのはタイミングもあるけど、 フロントエンドJavaScriptにおける設計とテスト をみたってのもある。

なにがしたいかって、JS において DOM 操作はともかく、ロジック部分はテストしたいんだよね。テストがないとリファクタリングするときに不安になる。

参考資料

件のスライドの @hokaccha さんの PixelGrid 社の CodeGrid を参考にした。この記事を書いた後にドットインストールの講座が出てきたから、今からだったらそれを見るのもいいかもしれない。

簡単なチュートリアル

面倒なので HTML はこんな感じ

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>

  <script type="text/javascript" src="./underscore.js" charset="utf-8"></script>
  <script type="text/javascript" src="./jquery-1.7.1.js" charset="utf-8"></script>
  <script type="text/javascript" src="./backbone.js" charset="utf-8"></script>
  <script type="text/javascript" src="./backbone.localStorage.js" charset="utf-8"></script>

</head>
<body>
backbone.js!
<ul class="todoList"></ul>
</body>
</html>

で、肝心の内容。本当は通信もみてみたかったけど、サーバーたてるの面倒なので backbone.localStorage を使用した。

// model の生成
var Todo = Backbone.Model.extend({
  name: null,
  localStorage: new Store("todos")
});
// model の集合である collection を生成
var Todos = Backbone.Collection.extend({
  model: Todo
});
// model のインスタンスを生成
var todo1 = new Todo({name: 'hoge'});
var todo2 = new Todo({name: 'moge'});
var todo3 = new Todo({name: 'toge'});
var todo4 = new Todo({name: 'ea'});
// collection のインスタンスを生成
var todos = new Todos();
// collection に model をつめこむ
todos.add([todo1, todo2, todo3, todo4]);

// UI コンポーネントと考えるといい。これは子要素の li タグ
var TodoListItem = Backbone.View.extend({
  tagName: 'li',
  // options ってつけるのが慣習っぽいけど、中身は object(dict) だよねっていう
  initialize: function (object) {
    // で、object のプロパティを指定して加える
    this.$el.text(object.text);
  }
});

// こっちは親要素の ul をとってくる
var TodoList = Backbone.View.extend({
  el: '.todoList',
  add: function (text) {
    // {text: text} ってのも微妙だけど、text プロパティに引数 text を加えるという話
    var item = new TodoListItem({text: text});
    this.$el.append(item.el);
  },
  collection: todos
});

// View のインスタンスを生成する
var todolist = new TodoList();

todolist.add('a');
todolist.add('b');
todolist.add('c');
todolist.add('c');

var TodoList = Backbone.View.extend({
  initialize: function() {
    this.collection.on('add', this.add, this);
  },

  add: function(todo) {
    var item = new TodoListItem({ model: todo });
    this.$el.append(item.el);
  }
});

雑感

Sencha Touch をガリガリやってて、「ああそういうことか」ってのが腑に落ちた。感覚としては「model, collection でデータを扱う。View で UI コンポーネントを生成する。それぞれは分離している」という感じ。だから疎結合にしやすい。ということなんだろう。

Event と Router は調べたけど触ってはいない。ただ、history.pushState とかつかっていい感じにしてくれるのは楽そうだね。

ちなみに

Sencha Touch で Ajax 通信してしまうとき idProperty がついてしまったりしてそれが API 仕様とあわなかったりするのは Backbone.js でもある話で、通信部分の URL の組み立ては結局自前でやる必要があったりする。GET, POST はいいけど PUT, DELETE とかを使うか否かってのもオプションにある。

あと AugularJS についてもちょっとだけドットインストールでみたりした。あれはもうちょっとライトなMVCのかたちなんだろうか?個人的には JSON 受け取ったら組み立てるだけって感じで、たしかにお手軽なんだけど本格的にサイトを組もうとすると大変そう。逆に言うと単純なサイトをお手軽につくるときには楽そう。

まあ

Backbone.js をつかって仕事する機会はそんなにないと思うけど、得るものはあったなという感じ

reStructuredText 用の簡単なスニペットをつくった

wiki や Redmine も markdown ではなくて reST だし、Sphinx つかうこともあるのでだいたい reST を使っている。プライベートや各種メモも Dropbox に todo.rst や anken.rst みたいな感じで管理してる。で、 riv.vim は高機能だけどちょっと面倒だし*1、なんかもっとてきとうに reST をかけないか。

探したけど

断片的になくはなかったが流用できるほどかというと微妙だった。Sublime Text 2 用にすこしあったけど、まあ今回は違うし。なくはなかったけど微妙だったのでてきとうにつくった。

前提

neocomplecache + neosnippet です

こんなの

たいした話ではない

snippet     quickstart
abbr        quickstart
prev_word   '^'
  ===========================
  ${1:content_name}
  ===========================

  .. content :: ${1:content_name}

  title1
  ====================================

  subtitle1
  -----------------------------------

  subtitle2
  -----------------------------------

  subtitle3
  -----------------------------------

  title2
  ====================================

  subtitle1
  -----------------------------------

  subtitle2
  -----------------------------------

  subtitle3
  -----------------------------------

  title3
  ====================================

  subtitle1
  -----------------------------------

  subtitle2
  -----------------------------------

  subtitle3
  -----------------------------------


snippet     contnents
abbr        contents
prev_word   '^'
  .. Contents :: ${1:contents_name}

snippet     title
abbr        title
prev_word   '^'
  ${1:title}
  ====================================

snippet     subtitle
abbr        subtitle
prev_word   '^'
  ${1:subtitle}
  -----------------------------------

snippet     code
abbr        code-block
prev_word   '^'
  .. code-block :: ${1:python}

    ${2:content}

snippet     link_raw
abbr        link_as_raw
prev_word   '^'
  \`${1:link}\`_

snippet     link
abbr        link_and_label
prev_word   '^'
  \`${1:title} <${2:link}>\`_

snippet     table_grid
abbr        grid_table
prev_word   '^'
  +------------------+------------+-----------------------+------------+
  |${1:cel}                  |            |                       |            |
  +==================+============+=======================+============+
  |                  |            |                       |            |
  +------------------+------------+-----------------------+------------+
  |                  |            |                       |            |
  +------------------+------------+-----------------------+------------+

snippet     field_list
abbr        field_list
prev_word   '^'
  :${1:text}  :  :  :
  :  :  :  :

snippet     list
abbr        list
prev_word   '^'
  -
  -
  -
  -

snippet     nested_list
abbr        nested_list
prev_word   '^'

  - ${1:text}
  -
  -
  -

snippet     caption
abbr        caption
prev_word   '^'
  [#]

snippet     image
abbr        image
prev_word   '^'
  .. image :: ${1:path}

snippet     strong
abbr        strong
prev_word   '^'
  **${1:text}**

コピペでもいいけど、だいたい議事録とかはじめにかくテンプレートってのが決まってれば全部スニペットにしてしまえばよい、という話。それっぽいものができるので、あとは議事録でもリストでも見出しでも書けばいい。

ついでに

Shougo/junkfile.vim · GitHub というのをいれた。コマンド一発で使い捨てで rst ファイルを呼び出して作ってしまえばいい。で、Vim で tmux で CUI 環境の Ubuntu Server でもとにかくコピペがしたい - 憧れ駆動開発 で書いたようにコピーしてしまえばいいし、Unite.vim はつかっているので履歴から呼び出せばよい。

ほんとうは

Firefox で(?:Vimperator|Pentactygl)外部エディタ呼び出ししてやるか、Chromeでも同じようなことができなくはないのでやるかするといい。とはいえ、宗教上の理由でOperaだし、Opera Extension は ChromeSafari と似ているとはいえまあ面倒なのでそこまではやっていない。赤福さんって人の wasavi っていうテキストエリアを vim 風に編集できる拡張もそれはそれでいいのだけど、やっぱり高機能な外部エディタを使いたい。

まあ

入力補完はテンプレートとかジェネレーターみたいな感じでどんどんやれるほうが好きだし、このスニペット自体の作成は20分くらいの作業でできることなのでよい。

*1:いちおうシンタックスに rest.vim はいれてはみている

Sphinx で日本語を含む PDF 出力をする

PDF を納める必要があったのでやった。Sphinx というか reST での日本語出力には若干の難があるらしい。けど、これも先人のとおりにやったら簡単にできた。

rst2pdf 方式

(8日目) Sphinx から PDF を生成してみよう (rst2pdf 編) - Hack like a rolling stone をまず参考にやってみた。pip install rst2pdf, PIL 環境構築, VLゴシックのようなフォントをいれる。 ja.json を作成して読み込ませる。PIL 環境の構築はハマりやすいから http://atasatamatara.hatenablog.jp/entry/20120803/1344001904 を参考にするといい。

結果としてはうまくいかなかった。後述の日本語のなんらかのパッチのはいった Sphinx だったらできたかもしれないけど、一部が文字化けした。あと、後述の latex 経由のほうが印刷物としてのオプションが豊富でいい感じだった。

LaTeX 経由で PDF 出力

(11日目) Sphinx から PDF を生成してみよう (LaTeX 編) - Hack like a rolling stoneLaTeX経由でのPDF作成 :: ドキュメンテーションツール スフィンクス Sphinx-users.jp を参考にした。

TeX 環境の構築

Mac なので MacTeX を導入した。若干重い。Linux なら LiveTex2011 あたりとからしい。Windwosはよくわからないけど、それっぽいのがあるらしい。

インストールしたらアップデートも必要。これもわりと時間がかかる。

sudo tlmgr update --self
sudo tlmgr update --all

TeX 関係の PATH を通す

そのまま。上記の記事だと2011年で若干違ったりするから、適宜置き換え。今回は2012年だった。

export PATH=$PATH:/usr/local/texlive/2012/bin/universal-darwin

Sphinx PDF 出力日本語パッチ済みのものをダウンロードする

ありがたや

pip uninstall Sphinx
pip install https://bitbucket.org/sphinxjp/website/downloads/Sphinx-1.1.3sphinxjp-latex.tar.gz

Sphinx の conf.py の設定

以下を設定する

# 言語の設定
language = 'ja'
# LaTeX の docclass 設定
latex_docclass = {'manual': 'jsbook'}

build!

make latexpfja

これで _build/latex/ 以下にPDFが出力される

継続的ビルド

Sphinx のビルドをファイル監視して自動で行う と同じ要領でファイル更新のたびにプレビュー

watchmedo shell-command --patterns="*.rst" --recursive --command='make latexpdfja'

とてもよい

LaTeX のオプション

印刷物としてそれっぽくいいかんじに出力するためには ビルド設定ファイル(conf.py) — Sphinx 1.1 (hg) documentation を参考にするといい。ページの改行とか、脚注とか、そういうオプションが豊富にあるので適宜設定する。

まあ

かなしみの Word や Excel 仕様書じゃなくて Sphinx によるよろこびの仕様書、よい。あと、仕様書もHTMLで十分じゃんと思ってたけど、なんかこう綺麗なPDFってちょっとうれしい感ある。