Ztráta typu objektu v JavaScriptu

od aichi E-mail

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ář

Komentář od: Martin Hassman [Návštěvník] E-mail
Martin HassmanTohle je jedna ze zákeřností JavaScriptu (resp. ani tak JavaScriptu jakožto jazyka, ale skriptování napříč okny, resp. dokumenty).

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.
21. 11. 08 @ 11:39

Napsat komentář


Vaše e-mailová adresa nebude zveřejněna.

Adresa Vašich WWW stránek bude zveřejněna.
(Konce řádku budou převedeny na <br />)
(Jméno, email a webová stránka)
(Dovolí ostatním uživatelům kontaktovat Vás prostřednictvím formuláře pro zprávy (Vaše e-mailová adresa NEBUDE zveřejněna.))