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

gotin blog

Whatever gotin wanna write

deep cloneを僕も考えてみた

追記

Dateはmutableなのでcloneするときはちゃんとコピーしてあげないといけない気がしてきたのでそのように修正。
Boolean、Number、String、RegExpはimmutableだからまぁそのまま返せばいいよねと。

ん?というかそもそもdeep cloneって基本は全部cloneつくるってことだよなぁ。。
そのまま返せばいいってなんかおかしいな。ってことでやっぱり全部コピーをつくることに。

あと、clone作ってから元を変更してもclone先は変更されないことをたしかめる簡単なテストもしているテストコードに修正。ちゃんとしたテストじゃないけど。

もひとつ追記

object=clone(object); - Thousand Years

↑こちらでもdeep cloneについて検討されていました。

追記以上

ここから本編

404 Blog Not Found:javascript - お伺い - Object.prototype.clone()
最速インターフェース研究会 :: JavaScriptにおけるdeep clone

deep cloneをする方法を僕も考えてみました。
目標は

  • Object.prototypeを汚さない
  • prototypeをたどって得られる要素もコピーする

二番目のは「firefoxならtoSourceでjson文字列をつくってevalすればいいじゃんね」法だとこれができないよねってってことで。

再追記

今更ながらwithを使ってブロックスコープを実現する方法を知りました(正確には、知っていたけど使い道が良くわかっていなかった)。これでループとクロージャつかったコードが書きやすくなるわけですね。

var clone = (function(){
  // They are atomic already
  var atomic = [Boolean, Number, String, Date, RegExp];
  var clone_funcs = {};
  for (var i = 0, l = atomic.length; i < l; i++){
    with({constructor:atomic[i]}){
      clone_funcs[constructor] = function(obj){
        return new constructor(obj);
      }
    }
  }
  atomic.push(Object, Array);
    // now the moment of truth!
  clone_funcs[Object] = function objectClone(obj){
    var cloneObject = new (obj.constructor);
    for (var p in obj) {
      cloneObject[p] = typeof obj[p] == 'object' ? clone(obj[p]) : obj[p];
    }
    return cloneObject;
  }
    // Array needs some special care
  clone_funcs[Array] = function(obj){
    var cloneArray = [];
    for (var i = 0, l = obj.length; i < l; i++) {
      cloneArray[i] = typeof obj[i] == 'object' ? objectClone(obj) : obj[i];
    }
    return cloneArray;
  }

  return function clone(obj){
    for (var i = 0, l = atomic.length; i < l; i++){
      if(obj instanceof atomic[i]){
        return clone_funcs[atomic[i]](obj);
      }
    }
    return obj;
  }
})();

まどろっこしい定義の仕方をしてるのは別の名前にしたいこともあるかなぁとか、オリジナルの関数定義のほとんどをコピーしてつくったからです^^;
[追記]ん〜別の名前にするならすればいいんだから、特に意味ないなぁ。あとで直すか。

とりあえずFirebugで試したらそこそこ動いてそうだったので載せちゃいました。多分おかしな部分があると思いますが。
数秒で考えたものなので、なんかすぐ訂正書くことになりそうな予感。。

やっぱまちがってるわ。

のでなおします。
なおしました。
けどFirebugで↓こんなテストをしただけなのでまだ間違いはあるかも。

function test(){
  function F(){}
  F.prototype.foo="foooo";
  var f= new F();
  var f2 = clone(f);
  f.hoge = "hoge";
  console.group();
  console.log("f");
  console.log(f);
  console.log("f2");
  console.log(f2);
  console.groupEnd();

  var d = new Date();
  var d2 = clone(d);
  d.setMonth(1);
  console.group();
  console.log("d");
  console.log(d);
  console.log("d2");
  console.log(d2);
  console.groupEnd();
}