Solution to the cross-frame instanceof scope problem
In my previous post I explained how I ran into the problem that dateB defined on frameB would return false if you execute dateB instanceof Date in frameA. I thought that this was because the data somehow got mangled when marshalled from one window to another. In fact it is nothing to do with that. The instanceof function is working entirely correctly, it’s just maybe not doing what you expect.
When you write (dateB instanceof Date) you are really asking was dateB created with function Date. Now when you add the implicit scope you begin to see why it doesn’t work the way you want. dateB was created in frameB with the function frameB.Date. So when you say dateB instanceOf frameA.Date as you implicitly are when you run this code in frameA, then of course it should and does return false.
If you explicitly call dateB instanceof frameB.Date it still returns true.
If you bear this in mind when calling functions that return data on other frames you can get round it. One approach which I have taken is shown here:
var frameB = top.frames["frameB"]; var dateB = crossFrame(frameB, frameB.getMeADate); alert(dateB instanceof Date); //give true function crossFrame(target, func, parms){ var result = func.apply(target, parms); result = tagDates.call(target, result); tagDates.call(target, parms); fixDates(parms); return fixDates(result); }
function fixDates(val){ if (val == null) return val; if (val["_date"]!==undefined) { return new Date(val); } if (typeof (val) != 'object') return val; if (val instanceof Array) { for (var j = 0,len=val.length; j < len; j++) { val[j] = fixDates(val[j]); }; return val; } for (var prop in val) val[prop] = fixDates(val[prop]);
return val; } function tagDates(val){ if (val instanceof Date) { val._date = true; return val; } if (typeof (val) != 'object') return val; if (val == null) return val; if (val instanceof Array) { for (var j = 0,len=val.length; j < len; j++) { val[j] = tagDates(val[j]); }; return val; } for (var prop in val) val[prop] = tagDates(val[prop]); return val; }
So we pass the target frame and the target function and the arguments array to the function crossFrame. Inside crossFrame we call the function on the target frame and get the results. At this point we are in calling frame scope. We then call tagDates using the target frame as scope. In this scope we can correctly identify the dates in the data. For each one we find, add an attribute so that we can identify it as a date later.
Then back in the calling scope we call fixDates which looks for the objects we tagged as dates and overwrites them with an equivalent Date created in the calling scope.
This means that when the data is returned from the call to crossFrame, any dates will have been re-created in the calling scope and any call to instanceof will work as expected.
The code above is not complete because it only handles dates. That’s all I have a problem with so it works for me but it could easily be extended for any other type because the underlying problem is not about types it is about scope.
[...] Posted in Javascript by julianjelfs on January 24, 2010 So in my previous posts here and here I described a problem I was having with Microsoft Ajax script library serialisation of Dates that [...]