Sapporo.js-2011.03.05を開催しました

Sapporo.js-2011.03.05 : ATND


この本もついに5章に入りました。

今回は蝶々本に載ってないところでも議論が盛り上がったので、話題に出たコードを貼り付けてみようと思います。


まずは4.15 メモ化から

用意されていたmemoizer関数に、キャッシュの破棄機能をつけてみよう、という話。

var memoizer = function(memo, fundamental) {
    var shell = function(n) {
        var result = memo[n];
        if (typeof result !== 'number') {
            result = fundamental(shell, n);
            memo[n] = result;
        }
        return result;
    }
    // リセットメソッド
    shell.reset = function(ini) {
        memo = ini;
    }
    return shell;
}

var fibonacci = memoizer([0, 1], function(fib, n) {
    return fib(n - 1) + fib(n - 2);
});
// 引数で与えたオブジェクトでリセット
fibonacci.reset([0, 1]);


また、本当に記憶したキャッシュを利用しているのかの確認のために、カウンターを設定してみます。

// 一回の関数呼び出しで計算が実行された回数
var t = 0;
// 実行された総回数
var total = 0; 

var fibonacci = memoizer([0, 1], function(fib, n) {
    t += 1;
    return fib(n - 1) + fib(n - 2);
});

// 降順で表示させているので、初回以降はすべて計算が行われていないことを確認する
for (var i = 10; i >= 0; i -= 1 ) {
    t = 0;
    // リセット機能の確認用
    // fibonacci.reset([0, 1]);
    document.writeln('// ' + i + ': ' + fibonacci(i));
    total += t;
    document.writeln(t);
}

document.writeln(total);


次は5章、継承なのですが、プロトタイプについてのおさらいです。
@bad_at_math さん、thxです!

// 一般的なプロトタイプ継承の例
var Child = function() {};
Child.prototype; // [Object object]
Child.prototype.hoge = "xxx";
var child = new Child();
child.hoge; // "hoge"

// オブジェクトからプロトタイプを設定する例(Mozillaのみ)
var c = {}, p = {};
p.__proto__ = {hoge: "ooo"};
c.__proto__ = p.__proto__
c.hoge; // "ooo"

また、間違えやすいのですが、以下の例はプロトタイプを設定できていない例です。
(ただ、"prototype"というプロパティが設定されるだけです。)

var c = {}, p = {foo: "bar"};
c.prototype = p;
c.hoge; // undefined


また、当日は、 @bad_at_math さんがLTをしてくださいました。

話の内容もすばらしかったのですが、何よりそれを即興でお話してくださったのがすごかったです。


また、当日の様子を @aloelight さんがまとめてくださっています。


次回開催は未定ですが、ご興味ある方はぜひいらしてください:-)
次回はp.56から始める予定です。


JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

Sapporo.js-2011.01.16を開催しました

Sapporo.jsでは、JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス を読み進めています。


今回は"4.11 コールバック"から進めていきました。
本ではさらっと書いてますけど、この辺りは内容が濃いです。


説明だけじゃなくて、もっとコードで動作を確認しながら進めていけばよかったなぁと反省してます><


また、今回は2人の方が発表してくださいました。

@havanaclub_ さん

@dont_cocoaさん

次回は3月開催予定です。
"4.15 メモ化"から進めていきます。


お時間ある方はお越しくださいませ!


JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

日常のスクリプト言語にJavaScriptはいかが?

JavaScript Advent Calender の14日を担当させていただきます。よろしくお願いします。


ところでみなさん、最近特に話題になっているあの言語、なんだかご存知ですよね?


そう、みなさん大好きなあの言語の名前は何でしょう!?



・・

・・・


『そうだね、JScript だね!』



はい、すみません。ごめんなさい。
というわけで(どういうわけで?)、ぼくの担当分は日常的にちょっと作業をする際の言語としてのJavaScriptをご紹介したいと思います。
今回はあえてWindows環境のみの紹介なので、JScriptといったほうが良さそうです。


ちょっとした作業で使うにはあまり便利とはいえませんが、もしかしたらこんな状況があるかも知れません。

  • Javaと聞いていたのに、行ってみたらCOBOLだった
  • PCには好きにソフトをインストールできない(業務規則的に)
  • というかそもそもネットワークにつながっていない(機密保持的に)
  • 使用してよいのはWindowsのみである


いやいや、ないとは言い切れませんよね!?
あぁ、恐ろしい。。。


そんなとき、ちょっとした作業に使える言語といえばMicrosoftが標準でサポートしている言語に限られてきてしまいます。
そうなったら、きっとみなさん迷わずJScriptを選ぶはず(tricknotes調べ)です!


さてさて、それではさっそく。


Windows Vista以降では、UACによってあらゆる操作が禁止されてしまいます。
『トイレの水を流そうとすると「本当に流しますか?」と聞いてくる』という有名なギャグがあるほどに。

そこでまずは管理者権限でJScriptを実行するところから。

(function() {
    // UACの識別に使用
    var key = "__uac";
    var args = WScript.Arguments;
    var length = args.length;
    
    var lastArg;
    if (length !== 0) {
        lastArg = args(length - 1);
    }

    if(key !== lastArg) {
        // 引数の引き回し用の配列
        var newArgs = [];
        for (var i = 0; i < length; i++) {
            newArgs.push(args(i));
        }
        // 識別用のキーを付与
        newArgs.push(key);
        
        // 元からの引数を引き継いで実行
        var sh = WScript.CreateObject("Shell.Application");
        sh.ShellExecute("wscript.exe", "\"" + WScript.ScriptFullName + "\" " + newArgs.join(" "), "", "runas", 1);
        // 初回の実行終了する
        WScript.Quit(0);
    }
})();

こちらを参考にさせていただきました。


これをjsファイルの先頭に記述しておくと、スクリプト実行時に管理者権限を与えるかの確認を求められます。
これでJScriptで何でもやりたい放題できる許可をもらいます。

うーん、スクリプト内で管理者権限で実行中かどうかを判断する方法って何かいいのがありますかねぇ。

スクリプト実行時の引数を取得するのがArgument関数となっているところに注意です。
(ちなみに存在しない引数にアクセスしようとすると例外が発生します)


また当然のように、CommonJSの仕様に準拠しているはずなどありません。
でもスクリプトが大きくなってくると、ファイルを分割したくなってくると思います。


しかし幸いながら、JScriptでローカルのファイルを読むことができます。
それならもう怖いものはありません。
evalでしのぎます。

function require(filepath) {

    var fs = new ActiveXObject("Scripting.FileSystemObject");
    var __dirname = WScript.ScriptFullName.replace(WScript.ScriptName, "");
    
    var file = fs.OpenTextFile(__dirname + filepath);

    var module = (function() {
        var module = {exports: {}};

        var source = [];
        while (!file.AtEndOfStream) {
            source.push(file.ReadLine());
        }

        eval(source.join("\n"));

        return module 
    })();

    return module.exports;
}

かなり力技ですが、node.jsっぽく外部ファイルを読み込むことができます。
(読み込まれる側のファイルの中から、一部の変数が見えてしまっていますが。。。)


例えば以下のようなファイルを用意します。

// demo.js
module.exports.hello = function() {
    return "hello world";
}

起動スクリプトから、以下のようにして呼んでやります。

var demo = require("demo.js");
demo.hello(); // "hello world"


さて、WindowsといえばExcel、ExcelといえばWindows。
ということでExcelもJScriptから記入してみましょう。

// エクセルファイル名
var filename = "demo.xls";

try {
    var excel = new ActiveXObject("Excel.Application");
    // エクセルファイルが画面に表示しない
    excel.Visible = false;
    // 確認ダイアログを表示しない
    excel.DisplayAlerts = false;

    try {
        // ファイルを開く
        var workbook = excel.Workbooks.Open(__dirname + filename);

        // ワークシートを取得
        var worksheet = workbook.Worksheets(1); // シート名でも指定できる

        var today = new Date();

        // 指定したセルへ値を設定

        // 日付を設定
        worksheet.Cells(1, 1).value = String(today.getYear()) + "年" + String(today.getMonth() + 1) + "月" + String(today.getDate()) + "日";

        // 名前を設定
        worksheet.Cells(2, 1).value = "tricknotes";

        // ファイルを保存
        worksheet.Save();
        
    } finally {
        // 後始末をしておかないとプロセスが残る
        if (workbook) {
            workbook.Close();
        }
    }
} finally {
    // 後始末をしておかないとプロセスが残る
    if (excel) {
        excel.Quit();
    }
}

Openメソッドの引数の説明はこちら


ちなみに、新規に作成する場合はこんな感じです。

var filename = "new_book.xls";

// 新規にワークブックを作成
var workbook = excel.Workbooks.Add();

try {
    var worksheet = workbook.Worksheets(1);
    worksheet.Cells(1, 1).value = "This file is created by JScript.";

    // ファイルを新規保存
    workbook.SaveAs(__dirname + filename);

} finally {
    workbook.Close();
}

この他にも、HTTPリクエストを発行したり、レジストリから値を取得したりと、わりと好き放題できます。
JScriptが何でもできるというよりは、ActiveXObjectをJScriptから使えていると表現した方がいいのかもしれません。
Windowsだけとはいえ、OS標準のシェルとしては十分魅力的な機能を備えていると思います。


JScriptを最優先で使おうとは思いませんが、Windows環境の特別な場合には選択肢の1としてアリかな、と思います。

Sapporo.js-2010.11.27の発表資料

開催からだいぶ時間が経ってしまいましたが、Sapporo.js-2010.11.27での発表資料を載せておきます。


デモアプリのコードはこんな感じです。


次回は2011/01/16(日)開催です。
よろしければお越しくださいませ!


Sapporo.js-2011.01.16

jsのプリミティブな型について

JavaScriptのプリミティブな型についての備忘録です。


(今回のテーマ)
以下の違いについて

var text = "hoge";
// 1)
String(text);
// 2)
new String(text);


1) String()

  • 戻り値はprimitiveな値
  • プロパティが要求されたときは、そのラッパーのオブジェクトのプロパティを返す
  • 値はimutableである

2) new String()

  • 戻り値はobject
  • valueOfメソッドで、そのプリミティブな値を返す
  • mutableである(自身を更新可能)
  • 状態(独自なプロパティ)を持てる

サンプルコードを書いてみますと、こういう感じです。

// リテラル表記
var a = "hoge";
// String関数で呼び出した場合
var b = String("hoge");
// Stringコンストラクタとして呼び出した場合
var c = new String("hoge");

typeof a;
// "string"

typeof b;
// "string"

typeof c;
// "object"

a["original"] = "foo";
a["original"];
// undefined
// "original"はa(primitiveなString)についてのラッパーのプロパティとして呼ばれたので、aに対しては設定されない

b["original"] = "foo";
b["original"];
// undefined
// aの場合に同じ

c["original"] = "foo";
c["original"];
// "foo"
// cはobjectなので、任意のプロパティが設定できる

// aとbは本質的に同じ

var d = new String("hoge");

d == a;
// true
// 型変換が行われて、どちらもString型になる

d === a;
// false
// 型が異なる

b === c;
// false
// 保持しているプリミティブな値は同じだが、オブジェクトが異なる

b == d;
// false
// オブジェクト同士なので、型変換が行われない

a.constructor;
// String
// aのラッパー(new String(a))のconstructorを返す

b.constructor;
// String
// aに同じ

c.constructor;
// String
// Stringコンストラクタでnewされたので、Stringを返す

といったところですか。


そもそも、重要になってくるのが、プロパティの参照順序です。
自身が指定したプロパティを持っていない場合、参照する順序は以下の順ようになります。


1) 自身がprimitiveな値である場合
自身のラッパのオブジェクトのプロパティを参照する

"hoge".valueOf(); // の場合
// "hoge"
// 自身のラッパオブジェクトのプロパティをたどる(存在しないので、さらにそのprototypeをたどる)
// (new  String("hoge")).valueOf(); と等価

(222).constructor;
// Number
// 自身のラッパオブジェクトのプロパティをたどる(存在しないので、さらにそのprototypeをたどる)
// (new Nunber(222)).constructor; と等価

2) 自身がオブジェクトである場合

({hoge: "foo"}).hoge;
// 自身がプロパティを持っている場合、自身のプロパティを返す

({}).toString();
// 自身がプロパティを持っていない場合、そのprototypeのプロパティをたどる
// "[object Object]"


この「自身のラッパのオブジェクトのラッパをたどる」挙動は、以下のようなメソッドを作成すると確認できます。

Object.prototype.self = function() {
    return this;
}

typeof ("hoge").self();
// "object"

("hoge").self().valueOf();
// "hoge"

たしかに、thisにはobjectが入ってきていますね。


JavaScriptでは、String、Number、Booleanがprimitiveな値として存在します。
比較演算ではこのあたりの挙動を注意しておく必要があるかなーと思いました。

自動で次のページを取得するjQueryプラグインautopagerをリリースしました

以前、@tmaedaさんがつぶやいていたのを思い出しまして、面白そうだったので作ってみました。


github:autopager


FireFoxだとアドオン入れて「はい、完了!」なのですが、この自動ページ取得機能をアプリケーションの機能として提供したい場合に便利かな、と。


使い方

<!-- jsファイルの読みこみ -->
<script href="jquery.js" type="text/javascript" />
<script href="autopager.js" type="text/javascript" />
<!-- 次のページを表示するDOMを指定して初期化 -->
<script type="text/javascript">
    jQuery(function($){
        $("body").autopager();
        // 現在のページのパラメータが"page"でない場合は以下のように指定
        // $("body").autopager({page: "_page"});
    });
</script>


ページの下の方までスクロールバーを持っていくと自動で次のページを取得してきます。
このとき、サーバ側でHTMLの断片を送るようにしてください。


注意
現在は一部使い方に制限があります。
> HTML内に、『次のページへのリンク』を表示させておいてください。(display:noneでも大丈夫です)
> CSSなどで、スクロールバーの幅を変更している場合は正しく動作しない可能性があります。


まだまだ未熟なプラグインですが、みなさま、よかったら使ってみてください!

大事なのは続けること、そしてそこから何を学ぶか

Sapporo.js-2010.08.07 が無事終了しました。
お越しくださったみなさま、本当にありがとうございました!


本日のLT資料を貼り付けておきました。
あまり中身はないので申し訳ないのですが(笑)

さて、今回はじめての開催ということでいろいろ手探りだったのですが、「よく訓練された方々」のおかげでなんとか乗り切ることができました。
ありがとうございます!!


次回以降の勉強会をもっともっといいものにしていくために、頑張っていこうと思います。



成功するにしても失敗するにしても、そこから得られるものをしっかりと探していきたいなぁ。