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の技
- 作者: JSサポーターズ
- 出版社/メーカー: 技術評論社
- 発売日: 2012/08/31
- メディア: 単行本(ソフトカバー)
- 購入: 38人 クリック: 1,796回
- この商品を含むブログ (11件) を見る
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 はつかっているので履歴から呼び出せばよい。
まあ
入力補完はテンプレートとかジェネレーターみたいな感じでどんどんやれるほうが好きだし、このスニペット自体の作成は20分くらいの作業でできることなのでよい。
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 stone と LaTeX経由での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'}
継続的ビルド
Sphinx のビルドをファイル監視して自動で行う と同じ要領でファイル更新のたびにプレビュー
watchmedo shell-command --patterns="*.rst" --recursive --command='make latexpdfja'
とてもよい
LaTeX のオプション
印刷物としてそれっぽくいいかんじに出力するためには ビルド設定ファイル(conf.py) — Sphinx 1.1 (hg) documentation を参考にするといい。ページの改行とか、脚注とか、そういうオプションが豊富にあるので適宜設定する。
まあ
かなしみの Word や Excel 仕様書じゃなくて Sphinx によるよろこびの仕様書、よい。あと、仕様書もHTMLで十分じゃんと思ってたけど、なんかこう綺麗なPDFってちょっとうれしい感ある。