ieの関数オブジェクトのスコープのきもちわるいやつ、もっときもちわるかった。
javascript gardenとかで紹介されてるこういうスコープ
http://bonsaiden.github.com/JavaScript-Garden/ja/#function.general
[名前付き関数式]のところ
var foo = function bar() { console.log(bar); // 動作する console.log(foo); // 動作する } console.log(bar); // ReferenceError foo(); // 動作する
割と知られてる話だけどfooとbarでは指してる関数オブジェクトは同じですけどスコープが違います。
これはいつものことながらieでは挙動が違います。
in ie
var foo = function bar() { console.log(bar); // 動作する console.log(foo); // 動作する } console.log(bar); // 動作する foo(); // 動作する
とまぁ本来なら外のスコープに露出しないはずのbarが漏れてます。
これだけならかわいいんだけど、とあるコードのパッチ書いてたらもっと気持ち悪いの発見した。
var foo = function bar() { console.log(foo === bar); // true (but ie<9 is false) } foo(); // 動作する
fxとかだとfooとbarは参照も含めて等価なんだけどこれをIEで実行するとfalseと。
これは結構はまりどころが多くて、jqueryのbindやoneとかみたいなのを実装しようとしたときに
fn.bind = ... fn.unbind = ... fn.one = function (eventName, handler) { var that = this; this.bind(eventName, function foo() { handler(); // 一回実行したらunbindする that.unbind(eventName, foo); }); }
とか書いた際に one の中でbindに委譲する際に渡されてる関数オブジェクトと その中でunbindするときに渡してるfooが別の参照になってうまく削除できない みたいなバグを起こします。
っていうか起こしてるコードと出会いました。
なのでご用心を。
メリークリスマス
世界にメリークリスマス。
http://nazomikan.com/archive/20121225/001/
npm xmasをフロントに移植してアニメーションしただけのごみですが一応クリスマスということで。
よく見てる人は気づいてるかもしれないけどサイトの方、ドメイン放置しすぎてrgpになってしまって急遽netにかわりました。
本当に死にたい。
rubyのArray#sortの挙動が怪しい件(あやしくなかった)
この挙動は別段問題なくて単純にコードブロックの評価順序によるものでした。
末尾に詳細を説明してますのでそちらを参照ください。
元記事
↓
Array#sortをブロック渡しで実行したときの挙動が変
# case 1 ary = ["1", "2", "10", "11", "3", "4", "23"] p ary.sort # result: ["1", "10", "11", "2", "23", "3", "4"] #case 2 ary = ["1", "2", "10", "11", "3", "4", "23"] p ary.sort do |a, b| a.to_i <=> b.to_i end # result: ["1", "10", "11", "2", "23", "3", "4"] #case 3 ary = ["1", "2", "10", "11", "3", "4", "23"] p ary.sort! do |a, b| a.to_i <=> b.to_i end # result: ["1", "10", "11", "2", "23", "3", "4"] #case 4 ary = ["1", "2", "10", "11", "3", "4", "23"] ary.sort! do |a, b| a.to_i <=> b.to_i end p ary # result: ["1", "2", "3", "4", "10", "11", "23"] #case 5 ary = ["1", "2", "10", "11", "3", "4", "23"] ary2 = ary.sort do |a, b| a.to_i <=> b.to_i end p ary2 # result: ["1", "2", "3", "4", "10", "11", "23"]case1は多分文字コードの出現順(だと思う)だから先頭一文字目の評価が優先されて
["1", "10", "11", "2", "23", "3", "4"]となるのは正しいと思う。
さらにcase 4,5も評価方法をintにキャストして比較するように命令してるので
["1", "2", "3", "4", "10", "11", "23"]となるのは正しいと思う。
問題はcase 2,3
こいつらもcase 4,5と同じように["1", "2", "3", "4", "10", "11", "23"]となることを期待してたんだけど得られた期待値は違った。
バグ? 僕の知らない仕様?
僕はjserで、rubyは今日からはじめたので正直よくわかってないけどjsとかでも文字列型の数値を要素にもった配列のsortは注意しないといけないところなのでrubyではどうなってるかなとおもってやってみたら闇にはいりこんでしまった。
ちなみにこの結果を確認した環境
ruby 1.9.2p180 (rev 30909) [x86_64-linux]
ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]
冒頭で追記したとおり、この現象はコードブロックの優先度の問題でした。
do..endの評価時の優先度が低くpの引数として扱われているようです。
コード的には以下のように解釈されている。
p(ary.sort) do |a, b| a.to_i <=> b.to_i end
ただしくコードブロックを適用させたsortの結果をdo..endを用いて評価させるのであればcase5のように一旦評価結果を変数に代入し、その後にpで評価する、もしくは以下のように優先度を考慮して書く必要があるようです。
p(ary.sort do |a, b| a.to_i <=> b.to_i end)
あと、do..endよりも{}ブロックのほうが優先度が高いので、そもそもdo..endを使わずに{}ブロックを用いる事でも解決できるよです。 っていうか普通はそっち使えって話らしい。
p ary.sort {|a, b| a.to_i <=> b.to_i }
canvasのlazyな描画
ご無沙汰してます、nazomikanです。
最近、趣味でフラクタルとかで遊ぶことが多く、フラクタル特有な計算量の多さに日々ブラウザが悲鳴をあげています。
そんなこともあってUI-Blockをさせないようにあれこれやった手法を紹介
まず最初に何も考えずに描画するコード
描画コード (github)
ディレクトリ構成 (github)
結果(ブラウザ落ちるかもしれないので新しいウィンドウで見ることをおすすめします)
http://nazomikan.com/archive/20121007/001/
まぁ当然大きな画像で、かつ1pxごとに計算して色を決めてるわけなので940*940ともなると死ぬほど処理がブロックされてブラウザ固まります。
残念ですね、非常に残念ですね、もはやブラクラです。
昔から大きな処理をするときはasyncにしなさいというのでlazyLoop(非同期なループ処理)を適用させたいと思います。
ちなみにlazyLoopってこんなん
/* * asyncなループ * @param {number} count ループのカウンター * @param {number} ループ回数 */ function lazyLoop(count, max) { // 毎ループごとの処理 // do something.. // 終了条件 if (count === max) { return; } // 非同期な再帰的処理 setTimeout(function () { lazyLoop(++count, max); }, 10); } lazyLoop(0, 100);
上記のlazyLoopと同様にsetTimeoutを使ってasyncにする
描画コード (github)
ディレクトリ構成 (github)
結果
http://nazomikan.com/archive/20121007/002/
遅い・・・。
まぁ、遅い・・・・。
ノンブロッキングになってブラウザが固まってるような印象はなくなりましたが、これでは描画が完了するまで待ってたらおじいさんになっちゃいます。
で、描画をある程度の単位でlazyにしたいなーと思うわけです。
というわけで描画の範囲をある程度の大きさに分割して描画していく方法
描画処理をキューにして、待ちキューをasyncに実行していきます。
二次元的な連続処理をキューに変換するコードをかいたのがこちら
(function () { var queue; // 100*100の二次元処理を25*25の処理ブロックに細分化 queue = createQueue(0, 0, 100, 100, 25, function (xMin, yMin, xMax, yMax) { var x, y; for (x = xMin; x < xMax; x++) { for (y = yMin; y < yMax; y++) { // do someThing } } }); // lazy output (function lazyLoop(i) { var task = queue[i++]; task(); if (i < queue.length) { setTimeout(function () { lazyLoop(i); }, 50); } }(0)); function createQueue(startW, startH, endW, endH, delta, callback) { var queue = [], startX, startY, endX, endY, xz, yz; for (startY = startH, endY = endH; startY < endY; startY) { for (startX = startW, endX = endW; startX < endX; startX) { // ここはjavascript>1.7ならlet使ったほうがいい xz = ((endW < startX + delta) ? endW : startX + delta); yz = ((endH < startY + delta) ? endH : startY + delta); //タスクを詰めたキューを作成している queue.push(function (x, y, xz, yz) { return function () { callback(x, y, xz, yz); }; }(startX, startY, xz, yz)); startX = xz; } startY = yz; } return queue; /* 返り値の中身 return [ function () { callback(0, 0, 25, 25); }, function () { callback(25, 0, 50, 25); }, function () { callback(50, 0, 75, 25); }, function () { callback(75, 0, 100, 25); }, function () { callback(0, 25, 25, 50); }, function () { callback(25, 25, 50, 50); }, function () { callback(50, 25, 75, 50); }, function () { callback(75, 25, 100, 50); }, function () { callback(0, 50, 25, 75); }, function () { callback(25, 50, 50, 75); }, function () { callback(50, 50, 75, 75); }, function () { callback(75, 50, 100, 75); }, function () { callback(0, 75, 25, 100); }, function () { callback(25, 75, 50, 100); }, function () { callback(50, 75, 75, 100); }, function () { callback(75, 75, 100, 100); } ]; */ } }());
これを応用して描画を細分化します。
描画コード (github)
ディレクトリ構成 (github)
結果
http://nazomikan.com/archive/20121007/003/
処理が100*100のブロックずつ描画されるようになったため、大規模なブロッキングも発生せずにサクサクと描画ができるようになりました。
こんな感じで、大きな描画などを発生させるときはlazyな描画でUI-blockから逃げましょう。
これは別にcsjsのcanvas描画のみに使えるというわけではなくbsjs(both side javascript)で二次元配列に再帰的処理を行いたいときとかにも使えます。
むしろssjsでのケースの方が再帰処理してる間、スレッドをブロックしてしまうと別ユーザーからのリクエストさばけないとかやばいことになりやすいのでこういうのが便利になってきます。
タスク分解されてるとスケールもしやすいですしね。
おわり
jenkinsとjslintを連携させてみたよ
jslintとjenkinsを連携させたかったので色々調べてみた。
jslint4javaっていうjslintのjavaバージョンがでていたのを発見したのでant使って連携してみました。
以下手順
とりあえず使用するプラグインいれておく
Jenkinsの管理 > プラグインの管理 > 以下のプラグインを追加
- Git Plugin
- ant
- Violations
jenkins再起動
次にjslintにかける対象のjobを作成します。
新規Jobの作成 > (Job名を決めてから)フリースタイル・プロジェクトのビルド > ok
プロジェクト名やもろもろ入力
今回はGitリポジトリ(Test)をjslintにかけるのリポジトリを設定
ちなみに今回対象にするリポジトリはこれ
(https://github.com/nazomikan/Test)
ビルドトリガのSCMをポーリングにチェックをいれる
スケジュールはcronの設定と同じ(今回は * * * * * )
ビルドの手順からantの呼び出しを選択してtargetをjslintにする
(後ほどつくるbuild.xml内でtargetをjslintって名前で作るので)
ビルド後の処理でReport Violationsにチェック
jslintの項目にbuild/logs/jslint.xmlと書く。
(これはlintをかけた結果のファイル置き場です。)
トップに戻ってビルドボタン(右っかわにある時計みたいなボタン)を押してビルドする。
antの設定してないので当然エラーでるけどworkspaceができあがる。
/var/lib/jenkins/jobs/Test(リポジトリ名) 以下にworkspaceディレクトリができてる
workspaceに移動してそこにbuild.xml作る
↓
<project default="jslint"> <target name="jslint" description="Run the JSLint tool on JS files"> <fileset dir="." id="jsfiles.raw"> <include name="**/*.js" /> <exclude name="**/*.min.js" /> </fileset> <pathconvert pathsep=" " property="jsfiles.clean" refid="jsfiles.raw" /> <exec executable="java" output="build/logs/jslint.xml"> <arg line="-jar build/lib/jslint4java.jar --report xml ${jsfiles.clean}" /> </exec> </target> </project>
jqueryとか外部のライブラリを使ってるテスト不要なやつとかはうまいことexcludeにいれるようにしましょう。
同一階層に以下のようにディレクトリ作る(jenkinsユーザが実行できるように実行権限をつけて)
workspace/ build.xml build/ lib/ logs/
lib以下にjslintのjava版のjarファイルを jslint4java.jar って名前にリネームして配置
ここからダウンロードできるよ。
jslint4java-2.0.2-dist.zip jslint4java-2.0.2 Featured
これを使う。
あと、ant使うのでいれてなければinstall (java/jdkも入ってなければいれる)
yum install -y ant
そして再ビルド
画像通り青色になってたら多分うまくいってる
ビルド番号をクリックしてそのビルドの詳細へ。
なんかjslintって書いたグラフがあるとおもうのでそれをクリック
jslintでエラーがでたファイルが列挙されてるのでファイル名をクリックして詳細へ。
エラーがでてるところにマウスカーソルあてるとjslintの結果がでてきます。
おわり。
ミドルウェアに弱かったりjenkins初めてだったり酔っぱらってたりするので不要な手順とかもっといい手順とかあったら教えて抱けるとうれしいです。
関数の再代入時のthis
オブジェクトのプロパティメソッドを一旦変数に代入してやるとthisの所在がかわってしまうのでそれについて
(function(){ var obj = { hoge: 'abc', moge : function() { console.log(this.hoge); } }; obj.moge(); // abc }());
普通にオブジェクトのプロパティを実行する分には問題なし
実行時にmogeメソッドのthisはobjをさしてるから
次に、オブジェクトのプロパティメソッドを変数に再代入する
(function(){ var obj = { hoge: 'abc', moge : function() { console.log(this.hoge); } }; var method = obj.moge; method(); // undefined }());
これはNG
なぜなら method = obj.moge の代入処理時にthisの値が = の左辺になってしまってうから
代入実行時にmethodはオブジェクトではないのでthisはグローバルをさしてしまう
というわけでthisを束縛してあげるproxy関数を作って間に一枚かますとok
(function(){ var obj = { hoge: 'abc', moge : function() { console.log(this.hoge); } }; function proxy(fn, obj){ return function(){ return fn.call(obj); } } var method = proxy(obj.moge, obj); method(); // abc }());
これならproxyのおかげでthisとしてobjを束縛してるのでok
引数とれないと色々不都合あるので引数とれるようにしてあげる。
(function(){ var obj = { hoge: 'abc', moge : function(a, b, c, d) { console.log(this.hoge); console.log([a, b, c, d]); } }; function proxy(fn, obj){ return function(/* arguments */){ var args = Array.prototype.slice.call(arguments); return fn.apply(obj, args); } } var method = proxy(obj.moge, obj); method(1, 2, 3, 4); // abc , [1,2,3,4] }());
これで大体の場合はok
追記
teramako先生からご指摘をいただきました!
新しいブラウザならjavascript1.8.5からFunction.prototype.bindが追加されているのでそれを利用するといいです。
(function(){ var obj = { hoge: 'abc', moge : function(a, b, c, d) { console.log(this.hoge); console.log([a, b, c, d]); } }; var method = obj.moge.bind(obj); method(1, 2, 3, 4); // abc , [1,2,3,4] }());
ちなみにこれはff4, ie9, chrome7,opera11.6以降から使えます
ご指摘ありがとうございました><
DOM操作系ライブラリのひな形
ちょろいライブラリを書く時とかぼくはこんな感じのひな形つくって、必要に応じてAPIを増やしていってます。
ま、あんまりAPI増えてくると管理の仕方を色々考え直さなきゃってなるけど
(function(win, doc){ win.lib = { // libライブラリの持ってるAPIを列挙 get: getNode }; // 要素の取得処理 function getNode(exp, context) { // クロスブラウザ化するならSizzleなどを使う var nodeList = (context ? context : doc).querySelectorAll(exp); return new NodeWrapper(nodeList); } // Nodeのラッパーオブジェクト function NodeWrapper(nodeList) { // ieはnodelistをsliceするとエラーでるので別途配列化関数が必要 this.items = Array.prototype.slice.call(nodeList); } NodeWrapper.prototype = (function(){ var api ={ // DOM操作系のapiを列挙 dump: dump }; // 上で列挙したDOM操作APIをここで定義 function dump() { console.log(this.items); } return api; }()); }(window, document));
こんな感じで使えます。
lib.get('h1').dump();
コンソールにh1タグが出力されます。
簡単な解説
win.lib = { // libライブラリの持ってるAPIを列挙 get: getNode }; // 要素の取得処理 function getNode(exp, context) { // クロスブラウザ化するならSizzleなどを使う var nodeList = (context ? context : doc).querySelectorAll(exp); return new NodeWrapper(nodeList); }
libオブジェクトにgetNodeというDOM取得関数を"get"という名前で登録しています。
getNodeはdocument.querySelectorAllをつかってDOMを取得してNodeWrapperコンストラクタで包んで返してくれます。
NodeWrapperはというと...
// Nodeのラッパーオブジェクト function NodeWrapper(nodeList) { this.items = Array.prototype.slice.call(nodeList); } NodeWrapper.prototype = (function(){ var api ={ // DOM操作系のapiを列挙 dump: dump }; // 上で列挙したDOM操作APIをここで定義 function dump() { console.log(this.items); } return api; }());
うけとったnodeの配列をプロパティとして保持してprototypeに定義したAPIから参照できるようにしています。
あとはprototypeにどんどんDOM操作系のAPIを列挙していけばok
これはひな形なのでdumpしかないけどメソッドを増やしていったりして肉付けしていくとおれおれライブラリが簡単にできますね。