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

素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: すかいぷ:javascripter_  か javascripter あっと tsukkun.net skypeのほうがいいです

循環参照を含むオブジェクトのディープコピー

function deepCopy(orig) {
  var clone = {};
  return (function (orig, clone, visited, ref) {
    for (var k in orig) {
      var v = orig[k];
      if (v instanceof Object) {
        var i = visited.indexOf(v);
        if (i == -1) {
          var o = {};
          visited.push(v);
          ref.push(o);
          clone[k] = arguments.callee(v, o, visited, ref);
        } else {
          clone[k] = ref[i];
        }
      } else {
        clone[k] = v;
      }
    }
    return clone;
  })(orig, clone, [orig], [clone]);
}

var obj = {a: 1, b: 2};
obj.self = obj;

var cp = deepCopy(obj);

cp.self.a = 2;
console.log(cp, cp.self == cp, obj.a, cp.a); //  Object a=2 b=2 self=Object true 1 2

Rubyだと、循環参照を含んでいてもMarshal.dumpがうまく動くので、

Marshal.load(Marshal.dump(obj));

で大丈夫。
JavaScriptの場合、オブジェクトを文字列化する方法として、uneval+eval、JSON.stringify+JSON.parseがある。JSONリテラルでは循環参照を含むオブジェクトを書くことはできない。unevalはFirefoxにしかないけど、Firefox用に拡張されたシャープリテラルを使って、

eval(uneval(#1={self: #1#}));

といった具合に、ディープコピーできる。