Ztráta typu objektu v JavaScriptu
od aichi
Psal jsem kód, který si mezi okny předává objekty pomocí Seznamácké JS knihovny JAK. Potom jsme zpřísnili typovou kontrolu v modulu předávání zpráv (signals) a najednou můj kód nefungoval. Ksakru proč?
...
Nejdřív malá ukázka, mějme dvě stránky, main a popup, kdy stránka main otevírá na klik popup a zde je na uživatelskou událost navázáno zavolání metody rodičovského okna. Main HTML:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>main</title>
<script type="text/javascript">
function addItem2(d) {
window.d = d;
console.log(d instanceof Object);
console.log(d);
console.log(d.data);
}
function openw() {
window.open('/messaging/popup.html', 'name', 'height=400,width=400,scrollbars=yes,resizable=yes' );
return false;
}
</script>
</head>
<body>
<a href="#" onclick="return openw();">Otevri okno</a>
</body>
</html>
Kód okna, vysílajícího zprávu vypadá takto:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>main</title>
<script src="/jak.js" type="text/javascript"></script>
<script type="text/javascript">
BLAF = SZN.HTTPRequest = SZN.ClassMaker.makeClass({
NAME: "BLAF",
VERSION: "1.1",
CLASS: "class"
});
BLAF.prototype.$constructor = function(){
this.data = 3;
}
BLAF.prototype.getData = function() {
alert(this.data);
}
</script>
</head>
<body>
<a href="#" onclick="return window.opener.addItem2({data:'blaf', w:window, f:function(){alert('aaa')}});">prenes parametr</a><br />
<a href="#" onclick="return window.opener.addItem2(new BLAF());">prenes parametr</a>
</body>
</html>
Prvním odkazem se předává anonymní objekt se třemi vlastnostmi, jsou to textový řetězec, objekt a funkce. Ve druhém případě se předává objekt vytvořený pomocí JAKového ClassMakeru, tudíž má vlastní konstruktor a prototypové metody.
V rodičovském (parent) okně je volána metoda addItem2, která do nějaké smysluplné konzole (FireBug) vypisuje co je to za objekt a provádí test zda je předáváný parametr objektem:
d instanceof Object
Zde je celý kámen úrazu. Tento test je negativní! JavaScript sprostě zapomene čehože je objekt instancí, prostě se tváří jako obálka s vlastnostmi. Zajímavé. Víme, že pro každé okno je vytvořena nová instance script engine a tudíž by mohl být problém v předávání informací, ale test na typ:
(typeof d) == 'object'
je úspěšný. Problém je, že typeof object má i pole, nebo řetězec vytvořen přes new String(). Pokud by se ztrácely informace, tak by asi neměl fungovat přenos metod, ale ten funguje. Na předaném objektu lze vyvolat jeho metody a ty pracují s jeho vlastnostmi. V příkladu máme v metodě volání funkce alert a ta je vyvolána dokonce v kontextu popup okna, kde byla definována, takže je jasné, že engine musí o druhém okně vědět.
Proč se ale ztratí informace o tom, čeho je objekt instancí je záhadou. Toto chování jsem si potvrdil jak ve FireFoxu tak i Internet Exploreru. Pokud používáte typovou kontrolu instanceof dejte si pozor odkud chodí kontrolované objekty. Chabou náhradou je kontrola přes typeof, popřípadě doplněná o kontrolu existence nějaké předem dané vlastnosti objektu.
Adresy zpětných odkazů pro tento příspěvek:
Trackback URL (right click and copy shortcut/link location)
1 komentář
On JavaScript nezapomíná, čeho je daný objekt instancí, on totiž není instancí objektu window.Object, ale objektu new_window.Object (tahle kontrola by vrátila true).
Vzhledem k tomu, že v JS lze modifikovat i vestavěné objekty, tak window.Object a new_window.Object mohou být zcela jiné objekty s jinými vlastnostmi.
Crockford ve své knize JavaScript - The Good Parts publikoval univerzální detekci pole (a není zcela triviální) napříč okny. Podobně by mohla jít udělat detekční metoda pro každý objekt. Alternativním způsobem by mohlo být vytvářet objekty děděné jen z jednoho (nejlépe asi rodičovského) okna (new parent.Object()), ale nikdy jsem to nezkoušel.
05. 11. 08 18.58:58, 
