以下ソース
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<title>setProp</title> | |
<body></body> | |
<script> | |
onload = function(){ | |
var ce=function(name){return document.createElement(name)} | |
var newDIV=function(){ var div=ce('div'); document.body.insertBefore(div); return div } | |
// プロパティを1つずつ変更する方法 | |
var div0=newDIV() | |
div0.innerText = 'div0' | |
div0.style.borderStyle = 'solid' | |
div0.style.borderWidth = '1px' | |
div0.style.borderColor = '#000' | |
div0.onmouseover=function(){this.style.backgroundColor = '#fcc'} | |
div0.onmouseout =function(){this.style.backgroundColor = ''} | |
// withで少し文字数を減らす方法 | |
with(newDIV()){ | |
innerText = 'div1' | |
with(style){ | |
borderStyle = 'solid' | |
borderWidth = '1px' | |
borderColor = '#000' | |
} | |
onmouseover=function(){this.style.backgroundColor = '#fcc'} | |
onmouseout =function(){this.style.backgroundColor = ''} | |
} | |
// setProp関数を使う方法 | |
setProp(newDIV(), { | |
innerText : 'div2', | |
style : { | |
borderStyle : 'solid', | |
borderWidth : '1px', | |
borderColor : '#000' | |
}, | |
onmouseover:function(){this.style.backgroundColor = '#fcc'}, | |
onmouseout :function(){this.style.backgroundColor = ''} | |
}, true) | |
// setProp関数を使って設定用objectを使いまわす方法 | |
var obj={ | |
style:{ | |
borderStyle : 'solid', | |
borderWidth : '1px', | |
borderColor : '#000' | |
}, | |
onmouseover:function(){this.style.backgroundColor = '#fcc'}, | |
onmouseout :function(){this.style.backgroundColor = ''} | |
} | |
setProp(newDIV(), setProp(obj, {innerText:'div3'}), true) | |
setProp(newDIV(), setProp(obj, {innerText:'div4'}), true) | |
// 配列を含むオブジェクトをsetPropでコピー | |
var obj={arr:[1,2,3]}, obj0={} | |
setProp(obj0, obj, true) | |
newDIV().innerText = obj0.arr.length | |
} | |
forIn = function(obj,fun){var name,res; for(name in obj){if(res=fun(name, obj[name])){return res}}} | |
setProp=function(objTarget, objProp, swSub, swVal){ | |
// swSubがtrueの場合、プロパティのプロパティを辿る流れでsetPropを再帰的に実行する。 | |
// falseの場合はforInでobjPropから列挙されたプロパティをobjTargetにコピーして終了。 | |
// swValueがtrueの場合、objPropが持つプロパティのうち中身が0やundefinedなどではないプロパティのみobjTargetにコピーする。 | |
// falseなら中身の種類を考慮しない。 | |
// 循環参照を含むオブジェクトをobjPropで渡された場合、かつ、swSubがtrueの場合は | |
// 無現ループになってしまう。循環参照を検出する方法は皆無ではないがプログラムが複雑になってしまうし | |
// objPropの規模に対して指数関数的に動作速度が低速化していくと予想されるので実装しない。 | |
forIn(objProp, function(name, v){ | |
if(swSub && forIn(objProp[name], function(n,v){return setProp(objTarget[name] || (objTarget[name]={}), objProp[name], true, swVal)}) ){ return } | |
if(swVal && !objProp[name]){ return } | |
objTarget[name] = objProp[name] | |
}) | |
return objTarget | |
} | |
// 上記の方が短くてシンプルだが、forInの呼出しの度に新しいfunctionを作成していてメモリ消費量が大きそう。 | |
// 下記は同じfunctionを使いまわすので階層が深くなったり枝分かれしまくっててもfunctionの数は初期から変化しない。 | |
setProp=function(objTarget, objProp, swSub, swValue){ | |
var funChildsCheck=function(){return true} | |
var arrT=[], arrP=[], 階層=-1 | |
var fun=function(n,v){ | |
if(swSub && forIn(v,funChildsCheck)){ | |
fun0(arrT[階層][n] || (arrT[階層][n]={}), v) | |
return | |
} | |
if(swValue && !v){return} | |
arrT[階層][n] = v | |
} | |
var fun0=function(objT, objP){ | |
arrT.push(objT) | |
arrP.push(objP) | |
階層++ | |
forIn(objP, fun) | |
階層-- | |
arrT.pop() | |
arrP.pop() | |
} | |
fun0(objTarget, objProp) | |
return objTarget | |
} | |
// 上記までの方法だと、objPropの中身が配列でもobjTargetには{}を元にしたオブジェクトを作成してしまう。 | |
// コンストラクタが配列と等しければ配列を元にするように変更。 | |
// その他のコンストラクタでも何かの問題が起きるかもしれないが、あらゆるケースに対応するものは作成できないので | |
// 必要なケースで必要なだけ対応する必要がある。例えば以下の場合、単純にコンストラクタを特定してnewで作成するだけではNG。 | |
// コンストラクタに合わせた対応が必要。 | |
// function myClass(初期値){ this.value = 初期値 } | |
// confirm((new (new myClass(0)).constructor()).value) | |
setProp=function(objTarget, objProp, swSub, swValue){ | |
var funChildsCheck=function(){return true} | |
var arrT=[], arrP=[], 階層=-1, arrC=[].constructor | |
var fun=function(n,v){ | |
if(swSub && forIn(v,funChildsCheck)){ | |
fun0(arrT[階層][n] || (arrT[階層][n]=v.constructor==arrC?[]:{}), v) | |
return | |
} | |
if(swValue && !v){return} | |
arrT[階層][n] = v | |
} | |
var fun0=function(objT, objP){ | |
arrT.push(objT) | |
arrP.push(objP) | |
階層++ | |
forIn(objP, fun) | |
階層-- | |
arrT.pop() | |
arrP.pop() | |
} | |
fun0(objTarget, objProp) | |
return objTarget | |
} | |
</script> | |
</html> |
プログラムごとに異なる独自オブジェクトについては無視するにしても、RegExpとかDateなどの組み込みオブジェクトぐらいには対応させた方が良いかもしれません。
後で時間ができたら追記します。
0 件のコメント:
コメントを投稿