思うところあって、 js-object-clone をリリースしました。

基本的には 404 Blog Not Found:javascript - ECMAScript 5 で Object.clone を実装してみた をブラッシュアップしたものですが、違いもあります。

  • DOM object の clone は取り下げ
  • prototype拡張はなし
    • Object.cloneを追加
  • 比較関数Object.equalsを追加
  • ES5完全対応
    • property descriptorの内容も比較/複写
    • .isExtensible, isSealed, isFrozen の状態も比較/複写

SYNOPSIS

var src = { name: 'dankogai', lang: ['perl'] };
var dst = Object.clone(src);        /* shallow copy */
log( Object.is(src, dst));          /* false */
log( Object.equals(src, dst) );     /* true */
dst.lang.push('javascript');
log(JSON.stringify(src.lang) );     /* ["perl","javascript"] because dst is shallow-copied */
dst = Object.clone(src, true);      /* deep copy */
dst.lang = dst.lang.reverse();
log( JSON.stringify(src.lang) );    /* ["perl","javascript"] */
log( JSON.stringify(dst.lang) );    /* ["javascript","perl"] */

もちろんカスタムオブジェクトもコピー可能ですし…

var Point = function(x, y) {
    if (!(this instanceof Point)) return new Point(x, y);
    this.x = x*1;
    this.y = y*1;
};
Point.prototype = {
    distance: function(pt) {
        if (!pt) pt = Point(0,0);
        var dx = this.x - pt.x;
        var dy = this.y - pt.y;
        return Math.sqrt(dx*dx + dy*dy);
    }
};
var src = Point(3,4);
var dst = Object.clone(src, true);
log( src === dst              );   /* false */
log( Object.equals(src,dst)   );   /* true */
log( dst.distance(Point(0,0)) );   /* 5 */

RegExpやDateもコピーできます。

var src = new Date(0);
var dst = Object.clone(src, true)
log( src === dst                );
log( Object.equals(src, dst)    );
log( dst                        );
src = /./g;
dst = Object.clone(src, true);
log( src === dst                );
log( Object.equals(src, dst)    );
log( dst                        );

むしろ必要だったのは、Object.equalsの方だったかも。入れ子になったオブジェクトの比較というのはテストを書く時にかなり多用するのですが、今ではidiomとなった感がある

var copyViaJSON = function(o){ return JSON.parse(JSON.stringify(o)) };

だときわどいケースに対応できないんですよね。undefinednullにしちゃうし、カスタムオブジェクトごとに.toJSON()を定義しないと使い物にならなかったり。_.cloneに至っては、そもそもdeep copy自体サポートしてないですし。ましてやES5のproperty descriptorまで対応しているものとなるとどこにも見当たりませんでしたし。

Enjoy (deeply)!

Dan the Man with too Many Objects to Compare and Copy

Source: