読者です 読者をやめる 読者になる 読者になる

時計を壊せ

駆け出しWebプログラマーの雑記

PerlまたはJavaScriptでObjectを文字列として評価したときの挙動をコントロールする方法。

例えば、Perlではオブジェクトを文字列として評価したときの挙動をコントロールしたい場合は、
以下のようにoverloadプラグマを利用して文字列化のためのメソッドをcoderefとして登録すると良い。

package MyURI;
use strict;
use warnings;
use utf8;

use Class::Accessor::Lite (
    rw  => [qw/scheme host path query/],
    new => 1,
);

use overload (
    q{""}    => \&as_string,
    fallback => 1,
);

sub as_string {
    my $self = shift;

    my $uri_str = sprintf '%s://%s%s', $self->scheme, $self->host, $self->path;
    $uri_str .= '?' . $self->query if $self->query;
    return $uri_str;
}

1;

このMyURIのObjectを以下のコードのように文字列として評価させようとすると、
内部的に上のoverloadプラグマで指定したas_stringメソッド*1が実行される。

use strict;
use warnings;
use utf8;

use MyURI;

my $uri = MyURI->new(
    scheme => 'http',
    host   => 'www.example.co.jp',
    path   => '/',
);

print $uri, "\n"; # http://www.example.co.jp/

$uri->query('aaa=bbb');

print $uri, "\n"; # http://www.example.co.jp/?aaa=bbb

Perlではこの方法は比較的よく知られている。*2


先日、業務でJavaScriptで同様の事をやりたくなり、調べたところ、
以下のようにtoStringメソッドをoverrideすると出来るらしい事が分かった。

var MyURI = (function () {
  var Constructor = function (opt) {
    this.scheme = opt.scheme;
    this.host   = opt.host;
    this.path   = opt.path;
    this.query  = opt.query;
  };
  Constructor.prototype.toString = function () {
    var uriStr = this.scheme + '://' + this.host + this.path;
    if (this.query)
      uriStr += '?' + this.query;

    return uriStr;
  };

  return Constructor;
})();

以下のように呼び出してみると、ちゃんと文字列化出来ているのが分かる。

var uri = new MyURI({
  scheme: 'http',
  host:   'www.example.co.jp',
  path:   '/'
});

console.log(uri + ""); // http://www.example.co.jp/

uri.query = 'aaa=bbb';

console.log(uri + ""); // http://www.example.co.jp/?aaa=bbb

参考記事
http://blog.livedoor.jp/dankogai/archives/50851804.html
http://blog.anselmbradford.com/2009/04/05/object-oriented-javascript-tip-overriding-tostring-for-readable-object-imprints/

*1:厳密には、overloadプラグマに渡すcoderefはメソッドとして呼び出される事を想定されたcoderefであればなんでも良いが、メソッドとしても呼び出せるようにしておく事を僕はオススメします。

*2:と僕は思っているが、もしかしたらそんな事は無いのかもしれない。