メッセージ欄

2010年5月の日記

一覧で表示する

functionについて
2010/05/21(金) 15:55 Javascriptはてブ情報 はてブに登録 はてブ数

JavaScript のブロックスコープと名前空間 « Mozilla Developer Street (modest)
での
  • b. 無名関数の関数スコープ変数をモジュール変数として利用する (古典) (function(){)()}
  • c. 無名コンストラクタの関数スコープ変数をモジュール変数として利用する new function(){}
についてから。
// Greasemonkeyでよく見かける
(function(){})()
// と
// jQueryで使われてるらしい
new function(){}
の違いという話になった場合、両方ともその場での中が評価されてるが、thisの値が異なることに注意
[JavaScript](function() )()とnew function() の違いがわかった / LiosK-free Blog
(function() {
    console.log(this);    // window
})();
new function() {
    console.log(this);    // [Object]
};
newキーワードの動作を簡単に見ると
  1. 真新しい空のオブジェクトを作る
  2. 作ったオブジェクトのプロパティをセットする
  3. thisが示すものを作ったオブジェクトにする。
  4. returnする。
(詳しくはこちらを JavaScript の new 演算子の意味: Days on the Moon )
となっているので、new function()の場合はthisがその場で作ったオブジェクトを示すため、もう一方とは違いwindowオブジェクトを示さない。(new functionの方が安全なのかなー)
new functionの場合でも引数は渡せるので、よく見かけるdocumentのエイリアスはこう書ける。
(function(doc) {
    p(doc);    // document
})(document);
new function(doc) {
    p(doc);    // document
}(document)
ついでに、new func()というのはfuncを引数なしでnewするという意味で、func()という実行した結果をnewするという意味ではないそうだ。
JavaScript Syntax探訪 - 枕を欹てて聴く
この例はイマイチ。
function func(doc) {
        console.log(this); // 1
        return function(){
            console.log(doc);     // document
        }
}
var a = func(document);  // 1で出されるのはwindow
var b = new func(document); // 1で出されるのはObject
(a() == b()) // true
次に無名関数をその場で実行しようと考えたときに
(function())()
//と
function()() // 結論から言うと意味が違う
の違いについて。
これを見ると分かったような分からない感じがする。
(function(){)() と function()() - IT戦記}
function文という用語は意味が分かりづらいので関数宣言としておきましょう。

上手くまとめられなかったので下の記事を読みましょう。
function から始まるとそれは関数宣言と見なされるため、
>|
function () , alert("書けない")
|<
というように関数宣言の後ろに続けて書く事はsyntax errorになる。
functionから始めなければ、JavaScriptは左から順番に評価されていくので続けて書ける。
>|
new function (), alert("書ける")
|<
つまり文の先頭がfunctionじゃなければ、関数宣言(function文)とならずfunction式となる。
>|
function()() // syntax error
new function (), function()() // エラーにならない
|<

function ~ が式になるか文になるかはどうやって決まる? - blanket log
上の記事のきちっとした補足
JavaScriptのfunction文とfunction式を考えてみる - webアプリの開発日記
文と式の違い
(function() ...)() の個人的な解釈 - hogehoge
(function() ...)での()はよく使う演算を優先させる演算子ではないという話。
(function() {)()とかnew function() とか - World Wide Wonderful}

今回みたいな(){}のはググれないのではてなウェブ検索を使うと便利だよ。

ソースコードが公開されているJavaScriptエンジン
2010/05/16(日) 18:37 Javascriptはてブ情報 はてブに登録 はてブ数

JavaScriptCore、SpiderMonkey、V8、Rhino

embed V8 - 枕を欹てて聴く
http://d.hatena.ne.jp/Constellation/20100408/1270663123
Embedder's Guide - V8 JavaScript Engine - JavaScriptで遊ぶよ - g:javascript
http://javascript.g.hatena.ne.jp/edvakf/20100407/1270626241

JavaScriptCore を自分でビルドして、遊んでみる - IT戦記
http://d.hatena.ne.jp/amachang/20080610/1213109852

SpiderMonkey (JavaScript-C) Engine
http://www.mozilla-japan.org/js/spidermonkey/
How to Download and Build V8 - V8 JavaScript Engine - Google Code
http://code.google.com/intl/ja/apis/v8/build.html
Rhino - MDC
https://developer.mozilla.org/ja/Rhino
/trunk/JavaScriptCore
http://svn.webkit.org/repository/webkit/trunk/JavaScriptCore/
JavaScriptCoreの元となったもの? KDE's JavaScript engine - Wikipedia, the free encyclopedia

safariがよく分からない。
SquirrelFishがsafari4からのJavaScriptCoreにあたる部分かな?
SquirrelFish – WebKit

SquirrelFish Extremeってのは多分JIT技術の総称なのかな。(FirefoxのJaegerMonkeyやTraceMonkeyみたいな)
Introducing SquirrelFish Extreme.ja – WebKit
用語が難しい
Firefox JavaScript技術まとめ - Spider/Trace/Jaegar/Nanojit/Nitro | エンタープライズ | マイコミジャーナル
http://journal.mycom.co.jp/news/2010/03/10/038/index.html
won't fix: JaegerMonkeyおさらい

setTimeout で実行する関数に引数を渡す
2010/05/12(水) 25:16 Javascriptはてブ情報 はてブに登録 はてブ数

setTimeout 関数は指定された時間の後に、コード文字列の評価または関数を実行する。
第一引数に渡せるものは、コードとなる文字列か関数オブジェクトを渡すことができる。
文字列を渡した場合はその文字列が eval によって評価されるが、eval される分処理が必要
になることや、文字列として渡されたものが安全かどうか確認できてない場合などがある
ためできる限り避ける方がよい。
今回は setTimeout に関数オブジェクトを渡し、その関数オブジェクトに引数を持たせて実
行させる方法を考える。
一つの方法としては匿名関数を使った方法がある。匿名関数で setTimeout を囲いスコープ
を作り、その匿名関数に引数を渡して実行させることでそれぞれのスコープ内で
setTimeout 関数が実行できる。
var ary = ["foo","bar","hoge"]
for(var i=0,len=ary.length;i<len;i++){
    (function(arg){
        window.setTimeout(function() { alert(arg); }, i * 1000);
    })(ary[i]);
}// "foo","bar","hoge"が一秒後づつ出現
new Functionを使うことでもでき、やっていることはevalとほぼ同様で文字列を関数として実行しているだけである。
内部的な動作は違うがやはり上記の匿名関数を使った方が良い。
15.3 Function オブジェクト
15.1 Global オブジェクト (The Global Object)
var ary = ["foo","bar","hoge"]
for(var i=0,len=ary.length;i<len;i++){
     window.setTimeout(new Function('alert("'+ary[i]+'")'), i * 1000);
}
最後にFirefox にはsetTimeout に第三引数があり、setTimeout(func, delay[, param1,
param2, ...]); という形で第三引数以降に渡した引数をfunc に渡し実行することが可能であ
る。しかし、このsetTimeout 関数はDOM Level 0 であるため標準化はされていない。
var ary = ["foo","bar","hoge"]
for(var i=0,len=ary.length;i<len;i++){
    setTimeout(alert , i*1000 , ary[i]);
}
他にもいろいろな方法がある。使えるブラウザが限定されるけど。
SCRAPBLOG : setTimeout のコールバック関数内でローカル変数を使用する
http://www.xuldev.org/blog/?p=136

  • Chapter 3. Dates, Time, and Timers prog*sig azu
    ■3.4  Converting an ISO 8601 Formatted Date to a Format Acceptable to the Date ObjectISO 8601からDateオブジェクトに変換してみる。 var dtstr= "2...

JavaScriptのyield
2010/05/08(土) 23:02 Javascriptはてブ情報 はてブに登録 はてブ数

いわゆるジェネレーターというものについて
yieldのsleep実装。他のブラウザでも使うフレームワーク
使えるのはFirefox限定?

実行するにはJS1.7をあつかえるものが必要になります。
Firebugは対応してないので、xqjs - ’ellaneousを使うと便利です。

*注
"→"は"つまり"程度の意味で読んどいてください。

MDCのジェネレーターで扱われているフィボナッチ数列のyieldを例にする。
function fib() {
  var i = 0, j = 1;
  while (true) {
    yield i;// 毎回ここで止まる
    var t = i;
    i = j;
    j += t;
  }
}
// *1
var g = fib();
for (var i = 0; i < 10; i++) {
// *2
  p(g.next());
}
*1
fib()なので、gに入るのはfibの実行結果。
通常ならreturnだが、yieldキーワードがfib内に存在する。
yield キーワードを含む関数(fib)はジェネレータという。
ジェネレーターを実行するとfib中身が実行されることなく、ジェネレータオブジェクト(ジェネレータ・イテレータ)という特殊なオブジェクトが返ってくる。(まだvar i=0なども起きてない)
→ まだ何も処理は行われてない
→ 返ってくるのはジェネレーターオブジェクト
*2
g.next()でyieldのところまで実行している。
yield i;はreturn i;と読み換えて(イメージ的)、返すのはi=0なので0、そしてそこで処理が止まる。
これで一回目のnext()は終わり。
次にg.next()すると、var t = i;から始まってwhileループしてるのでまたyieldのところで止まり、iの値を返す。
(g = g.next();としなくても、gは幾つ進んでるのを覚えている。)

つまり、yieldは渡された引数の値(iの事)を返して、同時にその行で処理を一時停止している。

ジェネレータオブジェクトは以下のメソッドを持っている

  1. next()
  2. send(n)
  3. close()
send()は引数なしだとnext()と等価だが、作ったジェネレータを一度next()してからでないとsend()メソッドを使うことはできない。

追記:
yieldの返り値とsend()の意味合いは次のコードに詰まってる。
function f() {
  p(1);// ... (a)
  var value = yield 2;// ... (b)        
  p(value);// ... (c)
}
var g = f(); // この時点で f は実行されず、ジェネレーターオブジェクトを返す
p(g.next()); // ここで (a) まで実行される→yieldに渡した引数(2の事)をreturnして一時停止
g.send(3); // valueに3をセットして、next()する。
// (b)の次の行から、つまり(c)から実行する。(そしてStopIterationの例外が起きる)
実行結果は1,2,3と出てくる。

yieldを使った非同期処理を同期的に書く方法。

runnableはジェネレータを覆う関数。
runnableの最後にnext();しておかないと、runnableの引数となる関数の仮引数(request)を実行した際には実体となってる関数function (t) { try{ o.send(t);catch(e) }}が実行されるので、sendはエラーになってしまう。
request(t)で実際にやることはo.send(t)になるので、引数に"hoge"としている場合、hogeを返す。
    function runnable(f) {
        var o;
        o = f(function (t) { try{ o.send(t); }catch(e){} });
        o.next();// ジェネレーターオブジェクトを作ったら一回目のnext();
    }
    runnable(function (request){
        // yieldで一時停止        
        var result = yield setTimeout(function () {request("hoge");}, 2000);// ここの内部的な動きがイマイチ
       
        p(result);// 2秒後にprintされる。
    });
もう少し掘り下げてみる。
上のコードは展開すると以下のような形になってる。
var f = function(request){
    // yieldで一時停止        
    var result = yield setTimeout(function () {request("hoge");}, 2000); 
    p(result);// 2秒後にprintされる。
}
var o = f(function(t){ try{ o.send(t); }catch(e){} });// ジェネレーターが返る
o.next();// 停止してたのを動かす→setTimeoutが実行される
o.next();でyieldによって止まっていた関数が動き出すので、setTimeoutが実行されて、
2秒後にrequest("hoge");つまりo.send("hoge");を実行することになる。
o.send("hoge")は resultに"hoge"をセットしてnext()となるので、resultには"hoge"が入り p(result);が実行される。

やっぱりイマイチピンとこないけど便利。

userChrome.jsでDOMContentLoaded
2010/05/06(木) 19:03 Javascriptはてブ情報 はてブに登録 はてブ数

GreasemonkeyではDOMContentLoaded時にスクリプトを実行できないので、userChrome.jsを使ってやる。勘違い。
サイト既存の何かが阻害して、画像を読み終わるまでGreasemonkeyスクリプトが動かない場合があったみたい。(GreasemonkeyがDOMContentLoadedにlistenされているのは知っていたけど、この現象を何回も見かけたので誤解したらしい)何だろこれ。

シンプルにまとめるとこんな感じで、DOMContentLoadedのイベントリスナーをつけてDOM構築時に実行できる。
var startDomContentLoaded = function(event) {
    var doc = event.target; // document
    var safeWindow = doc.defaultView; //window
    if(safeWindow.location.href.indexOf('判定URL') != -1) {
	// やりたい処理
    }
}
gBrowser.addEventListener('DOMContentLoaded', startDomContentLoaded, false);
今回やりたかったことは、任意のサイトにjQueryとjQueryプラグインを読み込ませて実行させるという事をやりたかった。
Greasemonkeyだと画像を読み込み完了しないと動作しないので、Greasemonkeyからは使えないプラグインなどがあるため、userChrome.js経由でGreasemonkey的な事をやってる。
var startDomContentLoaded = function(event) {
    var doc = event.target; // document
    var safeWindow = doc.defaultView; //window
    if(safeWindow.location.href.indexOf('http://mediamarker.net/') != -1) {
        var scriptjQuery = doc.createElement("script");
        scriptjQuery.type = 'text/javascript';
        scriptjQuery.src = "http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js";
        doc.body.appendChild(scriptjQuery);
        var scriptLazy = doc.createElement("script");
        scriptLazy.type = 'text/javascript';
        scriptLazy.src = "http://gist.github.com/raw/391939/9bccd0b20ed2866750c2749013893dea0e5f0e95/jquery.lazyload.js";
        doc.body.appendChild(scriptLazy);
    }
    evalInPage(function(){
        $("img").lazyload({ threshold : 200 });
    })
    function evalInPage(fun) {
        safeWindow.location.href = "javascript:void (" + fun + ")()";
    }
}
gBrowser.addEventListener('DOMContentLoaded', startDomContentLoaded, false);
script.onloadにしようとしたけど、動かなかった。(多分セキュリティ関係だと思う)

userChrome.jsでGreasemonkey的な事をやるときにwindowやdocumentに相当するものが何かと分かると作りやすくなると思う。

追記:GreasemonkeyがDOMContentLoadedで実行されることが確認できたので、Greasemonkeyで同じ事をやる場合、次のようになりました。

簡単ですね。