??? javascript is mutable !!!

There goes another few hours.. :-(
Please dont spread this around and dont say I wrote this after ~20 years of programming.
But JS is kinda mutable ????


var o1,o2
function fChange(arg){
    arg.k += '-new'
    return arg
}

o1 = {k: 'v1'}
o2 = {k: 'v2'}
o2 = fChange(o1)
console.log('1A) o1=' + JSON.stringify(o1))
console.log('1B) o2=' + JSON.stringify(o2))
//---
function fChange2(arg){
    arg = $.extend(true,{},arg)
    arg.k += '-new'
    return arg
}
o1 = {k: 'v1'}
o2 = {k: 'v2'}
o2 = fChange2(o1)
console.log('2A) o1=' + JSON.stringify(o1))
console.log('2B) o2=' + JSON.stringify(o2))

Results in:


1A) o1={"k":"v1-new"}
1B) o2={"k":"v1-new"}
//---
2A) o1={"k":"v1"}
2B) o2={"k":"v1-new"}

Conclusion:
Result 1A) is utterly amazing !
And terrifying !
Is the object o1 passed in by reference ? Yes it appears so.

~Solution:
Can be seen in fChange2(). Use jquery $.extend() to make a (deep or not) value copy.

arg = $.extend(true,{},arg)

And o1 remains unmutated, as we’d want it to be. See result 2A)
Alternative, not needing jquery, is:

arg = JSON.parse(JSON.stringify(arg))

Using the JSON lib also has the advantage that you dont have to handle cases where arg is Array containing objects in which case you’d need to use arg = $.extend(true,[],arg)

Please be silent and enjoy your knowledge !

Advertisements

atomize = KIS = SOC = smart !

Was writing a CList class and was gently reminded of some principles of programming.

  • atomize = KIS = SOC = smart !
  • atomize : for procs the less the better.
  • KIS : keep-it-simple.
  • SOC : seperation-of-concern : this proc is for this, that one for that.
  • smart! : a wise thing to do ; that will pay off ; rather sooner ; and surely later.

/*
Atomize = KIS = Seperation of concern = smart !
@IMPORTANT ! Strictly maintain:
- 1) Callformat: {keyword:xx, data:xx}
Use:
//eg: {"keyword":"ReqSetListFilter","data":{"filters":[{"dbfd":"groupNr","xval":"20"}]}}
//eg: {"keyword":"ReqGetListRecNr","data":{"recNr":1}}
Do not use:
//eg: {"keyword":"ReqSetListFilter","filters":[{"dbfd":"groupNr","xval":"10"}]}
//eg: {"keyword":"ReqGetListRecNr","recNr":0}
- 2) Do not return anything if not specifically a fGetXX call. eg: do not return recs on fEdit etc. Only return true, or false if something went wrong.
Except maybe some feedback info eg on fDoBulkUpdate(data.data) return ~nTouched. No, not even then. Use fGetListCount > fDoBulkUpdate > fGetListCount
*/

In the end you’ll end up with a nice CList class that, as by accident, quite easily handles filters (arrOriIdxs =[]) (flyweight pattern ?) and bulk updates.

Gotto run…

js: copy objects

//@Deps: jquery
//J. Resig:
//http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-a-javascript-object

	this.fCopyObj = function(){
		this.fcopyShallow = function (oldObject){
			return $.extend({}, oldObject);
		};
		this.fcopyDeep = function (oldObject){
			return $.extend(true, {}, oldObject);
		};
		return this;
	};

js: widgets

ha h.

januari is al zo’n beetje voor de helft voorbij gevliedt en ik ben nog steeds oude code aan het herschrijven. hopelijk zal blijken dat de wat stevigere fundamenten (json, data-models, observer pattern, widgets) zullen leiden tot snellere implementatie van functionaliteit !

gr,
m

//widget call-in function:

//this.mm = mm; //if extended
this.fcall_in = function(args){
fb(mm.container + '::fcall_in:' + fstringify(args));

if(!args.hasOwnProperty('reqAct')){
fb("ERROR no reqAct property." + mm.container);
}

var res = {}, tmp ='';
switch(args.reqAct){
case "reqGetData":
res = _fUIGetInputVals();
break;
case "reqSetData": // {navCode: reset|btnX , data : the-recs-as-from-converter}
if(args.navCode == 'reset' || args.navCode == 'btnCancel'
|| args.navCode == 'btnOk'){
if(args.navCode == 'reset' || args.navCode == 'btnOk'){
mm.nav.setQRecs(args.data.recs.length); //auto-moves 'first'
//so delete is a bit unfriendly bwth ftm... zz99
}

res = mm.nav.move('nomove');
if(args.navCode == 'btnOk' && mm.crudState == 'AOC'){res = mm.nav.move('last');}

$(mm.elbase + '.infolabel').html(res.navInfo);

if(args.data.recs.length>0){
_fUISetInputVals(args.data.ui2db,args.data.recs[parseInt(res.curr)]);
_fSetCrudState('PNAD');
}else{
_fUISetGetEmptyRecVals();
_fSetCrudState('A');
}
}else if(args.navCode == 'btnNext' || args.navCode == 'btnPrevious'){
(args.navCode == 'btnNext') ? tmp = 'next' : tmp = 'previous';
res = mm.nav.move(tmp);//res eg: {"curr":0,"qrecs":12,"navInfo":"1/12"}
$(mm.elbase + '.infolabel').html(res.navInfo);
_fUISetInputVals(args.data.ui2db,args.data.recs[parseInt(res.curr)]);
}
break;
/*case "reqSetEmptyData":
res = _fUISetGetEmptyRecVals();
break;*/
case "reqSetCrudState": //return as reqGetVals but with empties/defaults (?)
res= _fSetCrudState(args.crudState,'');
break;
case "reqGetNavInfo": //ao: handle parent(maps) move, so child (markers) can be synched
res = mm.nav.move('nomove');
break;
default:
fb("ERROR unknown reqAct:" + args.reqAct);
}
return res;
};

js; overriding class functions

//overriding class functions in instances

var myapp7 = function(){

//+- abstract class
function oo(id){
var mm = {name : id};
this.mm = mm;
this.fshout = function(p){
fb(mm.name + ': ' + p);
}
this.fshoutloud = function(p){
fb(mm.name + ' shoutloud:' + p.toUpperCase());
}
}

var p1 = new oo('p1');
var p2 = new oo('p2');
var p3 = new oo('p3');

//b:2 overrides
p1.fshout = function(p){
fb('p1 OVERRIDE ' + this.mm.name + ':' + p);
}
p2.fshout = function(p){
fb('p2 OVERRIDE ' + this.mm.name + ':' + p);
}
//e:2 overrides

p1.fshout('jah1');
p2.fshout('jah2');
p2.fshoutloud('jah2');
p3.fshout('jah3');

};//end app7
var x = new myapp7();
function fb(p){console.log(p);}

Results in:
p1 OVERRIDE p1:jah1
p2 OVERRIDE p2:jah2
p2 shoutloud:JAH2
p3: jah3

See also:
Creating JavaScript Classes, Part 3: Method Inheritance by Jason S. Kerchner on Feb 24, 2009

js; observer, subscriber pattern

//setting up callback functions 1onX, with subscribers

var otest6 = function(){

function oo(id){
var mm = {};
mm.id = id;
mm.callfns = [];
mm.callscopes = [];

this.fAddSubscriber = function(callfn,callscope){
mm.callfns.push(callfn);
mm.callscopes.push(callscope);
}

//simulate internally generated call-out
this.fcall_out = function(txt){
_fcall_out(txt);
};

function _fcall_out(txt){
fb(mm.id + ' request to shout:' + txt + ' To '
+ mm.callfns.length + ' subscribers');
for(var i = 0, ii= mm.callfns.length;i<ii;i++){
mm.callfns[i].apply(mm.callscopes[i],arguments);
}
}

this.fcall_in = function(args){
fb(mm.id + ' incoming:' + args);
};
}

var o1 = new oo('p1');
var o2 = new oo('p2');
var o3 = new oo('p3');

o1.fAddSubscriber(o2.fcall_in, o2);
o1.fAddSubscriber(o3.fcall_in,o3);
o1.fcall_out('do p2+p3 receive this ?');

o3.fAddSubscriber(o1.fcall_in, o1);
o3.fcall_out('does p1 receive this ?');
fb('COOL !')

};//end otest6
var app = new otest6();
function fb(p){
console.log(p);
}

Results in:
p1 request to shout:do p2+p3 receive this ? To 2 subscribers
p2 incoming:do p2+p3 receive this ?
p3 incoming:do p2+p3 receive this ?
p3 request to shout:does p1 receive this ? To 1 subscribers
p1 incoming:does p1 receive this ?
COOL !