追記
Dateはmutableなのでcloneするときはちゃんとコピーしてあげないといけない気がしてきたのでそのように修正。
Boolean、Number、String、RegExpはimmutableだからまぁそのまま返せばいいよねと。
ん?というかそもそもdeep cloneって基本は全部cloneつくるってことだよなぁ。。
そのまま返せばいいってなんかおかしいな。ってことでやっぱり全部コピーをつくることに。
あと、clone作ってから元を変更しても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(); }