2017年2月18日土曜日

複数のプロパティの値をまとめて変更する独自関数

obj.A = obj.B = obj.C = 1とか、そういうことではなく、例えばdivエレメントのinnerText、style.borderStyle、style.borderWidth…など複数のプロパティに対して個別の値を入れたい場合、この記事のソースを利用すると便利です。




以下ソース
<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>
view raw setProp.hta hosted with ❤ by GitHub



プログラムごとに異なる独自オブジェクトについては無視するにしても、RegExpとかDateなどの組み込みオブジェクトぐらいには対応させた方が良いかもしれません。
後で時間ができたら追記します。

0 件のコメント:

コメントを投稿