2012-08-31
BPStudy#60 ECMAScript5時代のJavaScriptライブラリ
JavaScript: 世界で最も誤解されたプログラミング言語(2001年)
JavaScript: The World’s Most Misunderstood Programming Language
2012年未だ誤解されているプログラミング言語JavaScript
嫌われている"JavaScript"とは何か?
嫌われている"JavaScript"と同世代の各言語のバージョンは?
- Python2.1
- Ruby1.6
- Perl5.6
- J2SE1.3
現在のJavaScriptはECMAScript5
どの環境で使える?
ECMAScript 5 compatibility table
ES5 features on iOS/Android's default browser - 愛と勇気と缶ビール
- IE9+(Strict modeを除く)
- Google Chrome13+
- 「変数名にゼロ幅スペース使用可能」を除く
- Firefox6+
- Safari5.1.4+
- 「変数名にゼロ幅スペース使用可能」を除く
- 込みなら6+
- 「変数名にゼロ幅スペース使用可能」を除く
- MobileSafari iOS6+
- Android 標準ブラウザ 4.0+
- 「変数名にゼロ幅スペース使用可能」を除く
- strict modeを除く
- 込みなら4.1+
- Opera12+
- 「変数名にゼロ幅スペース使用可能」を除く
shimライブラリを使えば対応環境はもっと増える
- IE9+(Strict modeを除く)
- Google Chrome13+
- 「変数名にゼロ幅スペース使用可能」を除く-Firefox6+
- Safari5.1+
- 「変数名にゼロ幅スペース使用可能」を除く
- 込みなら6+
- 「変数名にゼロ幅スペース使用可能」を除く
- MobileSafari iOS5.0+
- Opera12+
- 「変数名にゼロ幅スペース使用可能」を除く
何が出来る?
- strict mode
- Array#map等のarrayの反復メソッド
- その他builtin objectの便利メソッドが若干
- native JSON
- Function#bind
- thisと引数の束縛
- Object.create
- object保護関数群
- Object.preventExtensions
- propertyの追加定義禁止
- Object.seal
- propertyの追加定義・再定義禁止
- Object.freeze
- propertyの追加定義・再定義・値の変更禁止
- Object.preventExtensions
- Object.Object.defineProperty
- writable
- enumerable
- configurable
- value
- get
- set
ECMAScript5の豊かさを活かしていきたい
ECMAScript5をターゲットにしたライブラリ、BeautifulProperties.js
v0.1.0を先ほどリリースしました
2012-07-26
インスタンスに依存した初期値を持つ書き換え可能propertyの定義
インスタンスに依存してなければこれで済むから簡単ですよねー
function A(){} var proto = A.prototype; Object.defineProperty(proto,'key',{ value : 'default', writable : true });
インスタンスに依存している場合はprototype定義時にそのインスタンスが存在しないのでvalueで初期値を定義できません。
単純なコードだと、実際の値を保持する別propertyとget/setを定義して、まだ保持していなかったら設定するとかそうなるでしょう。
function foo(val) { // valの内容によって戻り値が変わると思いねえ return 'default'; } function A(){} var proto = A.prototype; Object.defineProperty(proto,'key',{ get : function () { var self = this; if (self._key === undefined) { self._key = foo(self); } return self._key; }, set : function (val) { var self = this; self._key = val; } });
しかし、無駄にpropertyを増やしたくありません。
こうします。
prototypeに対してconfigurable:trueにしてget/setありのpropertyを定義、
その中でインスタンスの(!=prototypeの)propertyを上書き定義します。
function foo(val) { // valの内容によって戻り値が変わると思いねえ return 'default'; } function A(){} var proto = A.prototype; Object.defineProperty(proto,'key',{ get : function () { var self = this; var val = foo(self); Object.defineProperty(self,'key',{ value:val, writable:true }); return val; }, set : function (val) { var self = this; Object.defineProperty(self,'key',{ value:val, writable:true }); }, configurable : true });
ばっちり動きますね
a= new A; b= new A; a.key; // 'default' a.key= 1; a.key; // 1 b.key; // 'default'
2012-07-25
JavaScriptでの非同期関数合成
Unserscore.jsや互換ライブラリのLo-Dashを使うと関数合成が出来ます。
複数個の関数があって、関数を呼び出した結果を使って関数を呼び出して…っていうのを1個の関数にします。
ドキュメントの例を見れば分かるかと。
簡略化のために関数合成の対象になる関数を1引数・戻り値ありの関数とします。
これを非同期処理をする関数に当てはめるとcallbackを含む2引数・戻り値なしの関数が当てはまるでしょう。
まず、logを出力するcallback関数を定義しましょう。
function log(result){ console.log(result); }
次にcallbackを含む2引数・戻り値なしの関数を定義します。別に非同期処理はやっていないです。
// 1を足す function add1(callback,arg){ callback(arg+1); } // 2をかける function mul2(callback,arg){ callback(arg*2); }
実行してみましょう
add1(log,4); // 5 mul2(log,5); // 10
結果を引き回す形で呼んでみましょう。
add1(function(result){ mul2(log,result); },4); // 10
呼び出しが深くなるとcallbackのネストが段々鬱陶しくなります。
add1(function(result){ mul2(function(result){ add1(log,result); },result); },4);
Function#bindによる部分適用に置き換えます。
add1.bind(null,mul2.bind(null,log))(4); // 10
べた書き部分を汎用にしてみます。
(function(){ var slice = Array.prototype.slice; var funcs = slice.call(arguments); return funcs.reduceRight(function(prevResult,current){ return current.bind(null,prevResult); }) })(add1,mul2,log)(4);
今回やりたいことは、callbackを含む2引数・戻り値なしの関数をn個合成してcallbackを含む2引数・戻り値なしの関数にすることなのでこれは違います。
で、こうします。
function noop(){} function composeAsync(){ var slice = Array.prototype.slice; var funcsOrig = slice.call(arguments); return function (callback,arg) { var funcs = funcsOrig.slice(); funcs.push(callback || noop); funcs.reduceRight(function(prevResult,current){ return current.bind(null,prevResult); })(arg); }; } 実行してみましょう
composeAsync(add1,mul2)(log,4); // 10 composeAsync(add1,mul2,add1)(log,4) //11
「callbackを含む2引数・戻り値なしの関数をn個合成してcallbackを含む2引数・戻り値なしの関数にする」ので合成した関数も合成対象にできます。
var composed = composeAsync(add1,mul2);
composeAsync(composed,composed)(log,4);
この縛りでは合成対象の関数の汎用性に問題があるという場合は、
2012-04-17
packerで圧縮されたJavaScriptのdebug方法
minifyされたJavaScriptは各種開発者ツールのdeminifierを使えば整形された状態でdebug出来ますが、packerで圧縮されたJavaScript(以下packed JS)はどうかという話。
Google Chromeの開発者ツール前提で話をするので他のブラウザについては誰か調べてみてください。
packed JSをdebugする際の壁はそれがevalで実行されることですが、Google Chromeの開発者ツールではevalで実行されたJSもdebug実行出来ます。
問題はどうやってdebug実行に持っていくかですが、globalから辿れる何らかのAPIがあるなら呼び出し元でブレークポイントを貼ってstep intoすれば良いです。
その場実行されるようなJSの場合はそれもできないので工夫が必要です。
以下は古いjQueryを使った例でそのような工夫は本来不要ですが、その場実行されるJSに対して使えるテクニックです。
その場実行されるpacked JSをdebug実行する(Google Chrome開発者ツール)
ターゲットとなるpacked JSが読み込まれる前に何らかの手段でJSを読み込めるようにしましょう。
CocProxyあたりで差し替えられるようにすれば良いと思います。
そこで、ターゲットの中で使われているであろうbuiltin methodを差し替えるJSを読み込ませ実行します。
失敗したら使われていそうなmethodを変えてやっていきましょう。
元と同じ動作でdebugger statementが仕込まれたmethodに差し替えます。
(function() { var proto = String.prototype; var x = proto.replace; proto. replace = function() { debugger; return x.apply(this, arguments); }; })();
packed版jQueryを読み込ませてみましょう。
s=document.createElement('script');s.src='http://jqueryjs.googlecode.com/files/jquery-1.2.6.pack.js';document.body.appendChild(s);
早速debugger statementでbreakされますが、
step outするとpacked JS自体の実行だと分かるのでどんどん実行してしまいましょう。
さて、ここからが本番です。scriptタブの下部の「{}」をクリックして有効(青)にしましょう。これが標準で使えるdeminifierです。
適当に以下のようなコードを実行して、packed版で読み込んだjQueryのコードを動かしましょう。
$('<div>');
step outすると整形されたターゲット(jQuery)のコードが見れます。
以降はscriptタブ上部のソース選択で(program)というものがあるのでその中でconsoleから実行したものではない方を選べばいつでも整形されたターゲットのコードが見れますし、以下のように好きなところにブレークポイントを貼ってdebug実行することも出来ます。
2011-12-12
MacHGでのrebase onto Mercurial Advent Calendar 2011
Mercurial Advent Calendar 2011 - PARTAKEの12日目は文殊堂がお送りします。
深夜35時などという遅くに申し訳ありません。
今日はSourceTree無料キャンペーンによって存在意義が危うくなった感のあるMacHGも得意な箇所は凄いんだよ、ということで
MacHGでrebase ontoをやるお話です。
Mercurialのrebase
rebaseというとGitのコマンドという印象が強いですが、
Mercurialでもビルトインのextensionを有効化するだけで使用できます。
話の都合上ついでにいくつか有効化しておきましょう。
[extensions] rebase = mq = graphlog =
rebase onto
さて、今日お話しするのはgit rebase --onto相当のことです。
まず以下のglogを見てください。
このmami branchから分岐しているmado branchをhomu branchから分岐するように改変します。
$ hg glog @ changeset: 3:09876c6c806e | branch: homu | tag: tip | parent: 0:663f5d36f850 | user: monjudoh <monjudoh@gmail.com> | date: Tue Dec 13 09:51:41 2011 +0900 | summary: branch homu | | o changeset: 2:917591f587ee | | branch: mado | | user: monjudoh <monjudoh@gmail.com> | | date: Tue Dec 13 09:44:03 2011 +0900 | | summary: branch mado | | | o changeset: 1:eaa1adaa875b |/ branch: mami | user: monjudoh <monjudoh@gmail.com> | date: Tue Dec 13 09:42:29 2011 +0900 | summary: branch mami | o changeset: 0:663f5d36f850 user: monjudoh <monjudoh@gmail.com> date: Tue Dec 13 09:41:46 2011 +0900 summary: first commit
とりあえず--sourceと--destを指定してみるとただのmergeになってしまいます。
$ hg rebase --source mado --dest homu saved backup bundle to /Users/monjudoh/Dropbox/example/.hg/strip-backup/917591f587ee-backup.hg $ hg glog @ changeset: 3:4f7d2a838aa9 |\ branch: homu | | tag: tip | | parent: 2:09876c6c806e | | parent: 1:eaa1adaa875b | | user: monjudoh <monjudoh@gmail.com> | | date: Tue Dec 13 09:44:03 2011 +0900 | | summary: branch mado | | | o changeset: 2:09876c6c806e | | branch: homu | | parent: 0:663f5d36f850 | | user: monjudoh <monjudoh@gmail.com> | | date: Tue Dec 13 09:51:41 2011 +0900 | | summary: branch homu | | o | changeset: 1:eaa1adaa875b |/ branch: mami | user: monjudoh <monjudoh@gmail.com> | date: Tue Dec 13 09:42:29 2011 +0900 | summary: branch mami | o changeset: 0:663f5d36f850 user: monjudoh <monjudoh@gmail.com> date: Tue Dec 13 09:41:46 2011 +0900 summary: first commit
Gitのrebaseのようにやるには--detachを付けます。
しかし、mado branchが消えてしまいました。あんまりだよ、こんなのってないよ。
$ hg rebase -s mado -d homu --detach saved backup bundle to /Users/monjudoh/Dropbox/example/.hg/strip-backup/917591f587ee-backup.hg $ hg glog @ changeset: 3:15acace3840a | branch: homu | tag: tip | user: monjudoh <monjudoh@gmail.com> | date: Tue Dec 13 09:44:03 2011 +0900 | summary: branch mado | o changeset: 2:09876c6c806e | branch: homu | parent: 0:663f5d36f850 | user: monjudoh <monjudoh@gmail.com> | date: Tue Dec 13 09:51:41 2011 +0900 | summary: branch homu | | o changeset: 1:eaa1adaa875b |/ branch: mami | user: monjudoh <monjudoh@gmail.com> | date: Tue Dec 13 09:42:29 2011 +0900 | summary: branch mami | o changeset: 0:663f5d36f850 user: monjudoh <monjudoh@gmail.com> date: Tue Dec 13 09:41:46 2011 +0900 summary: first commit
branchを保持してrebaseしたいのであれば--keepbranchesを付けましょう。そろそろoptionの長さにイライラしてきていると思いますが、ひとまずちゃんとrebase ontoに成功しました。
$ hg rebase -s mado -d homu --detach --keepbranches saved backup bundle to /Users/monjudoh/Dropbox/example/.hg/strip-backup/917591f587ee-backup.hg $ hg glog @ changeset: 3:1548007f5eaa | branch: mado | tag: tip | user: monjudoh <monjudoh@gmail.com> | date: Tue Dec 13 09:44:03 2011 +0900 | summary: branch mado | o changeset: 2:09876c6c806e | branch: homu | parent: 0:663f5d36f850 | user: monjudoh <monjudoh@gmail.com> | date: Tue Dec 13 09:51:41 2011 +0900 | summary: branch homu | | o changeset: 1:eaa1adaa875b |/ branch: mami | user: monjudoh <monjudoh@gmail.com> | date: Tue Dec 13 09:42:29 2011 +0900 | summary: branch mami | o changeset: 0:663f5d36f850 user: monjudoh <monjudoh@gmail.com> date: Tue Dec 13 09:41:46 2011 +0900 summary: first commit
ただ、id:monjudoh:20110801:1312179612 で書きましたがMercurialで歴史改変を行う場合は、
元のchangesetsを保持するoptionを付けて行い、正常に出来たことを確認した上で古い方の歴史をstripで削除する
のをお勧めします。
今回の場合だと、-keep optionを付けて実行し、確認後に古いほうをstripすることになります。
$ hg rebase -s mado -d homu --detach --keepbranches --keep
正直かったるいですね。
思うにrebase ontoという操作はコマンドラインで行うには複雑すぎる操作なのではないかと思います。
本家のgit rebase --ontoはoptionこそ少ないですが、3引数の順番とか覚えてられません。
MacHGの強力な歴史改変サポート
MacHGではrebase,mq(stripのみ),collapse,histeditといった歴史改変系のextensionを強力にサポートしています。
このような履歴になっている状態で、右クリックからRebase changesets…を選択し、
移動元と移動先をグラフからそれぞれ選択し、必要に応じて--keepbranches --keep相当のチェックボックスを選択しRebaseボタンを押すだけです。
無事元branchを保持したrebase ontoが出来ました。
まとめ
MacHGの歴史改変操作のUIはとても素敵です。
皆様も活用してみてはどうでしょうか。
後、よくわかってない奴は歴史改変とかすんな。
では13日目の @ponkotuy さんよろしくお願いします。









