JavaScript でキーワード引数

なんか、輪講が終わったら Dan さんからトラックバックがきているし
で、id:mopemope さんの JavaScript でのキーワード引数風な関数を見てなるほどと思った。まぁ、要するに関数を文字列化して無理矢理引数名を取り出すという手法なんだけど、インスタンスのメソッドや引数がない関数に対応していないような感じだ。その辺を改良して、パラメータの過不足はエラーになるようにしてみた。

function KeywordArgumentError(message) {
    this.message = message;
}
KeywordArgumentError.prototype.name = 'KeywordArgumentError';
KeywordArgumentError.prototype.toString = function() {
    return this.name + ': ' + this.message;
}

Function.prototype.get_argument_names = function() {
    var m = this.toString().match(/\(([0-9A-Za-z_, ]*)\)/);
    if (m[1].length > 0) {
        return m[1].split(/\s*,\s*/);
    }
    else {
        return [];
    }
};

Function.prototype.keyword = function() {
    var func = this;
    var names = this.get_argument_names();
    var positions = [];

    for (var i in names) {
        positions[names[i]] = i;
    }

    return function(args) {
        var real_args = [];
        var n = 0;
        for (var name in args) {
            if (name in positions) {
                real_args[positions[name]] = args[name];
                n++;
            }
            else {
                throw new KeywordArgumentError("function got unknown parameter `" + name + "'");
            }
        }

        if (n != names.length) {
            throw new KeywordArgumentError("function takes arguments named " + names);
        }

        return func.apply(this, real_args);
    };
};

こんな感じに使う。ちなみに SpiderMonkey でしか動作確認していない。

function Foo(x) {
    this.x = x;
}

Foo.prototype.set = function(name, val) {
    this[name] = val;
}.keyword();

Foo.prototype.g = function() {
    return (20);
}.keyword();

var bar = function(x, y) {
    return "(" + x + "#" + y + ")";
}.keyword();



var f = new Foo(10);
alert(f.x);
f.set({name: 'x', val: 20});
alert(f.x);
alert(f.g());

alert(bar({y: 10, x: 20}));