[JS] problem z metodą w prototypie

0

Prosty kod:

<html>
<head>
<script type="text/javascript">
Object.prototype.setAttributes = function (changes) {
	for ( var p in changes ) {
		if ( typeof changes[p] === 'object' ) this[p].setAttributes(changes[p]);
		else if ( p !== 'setAttributes' ) this[p] = changes[p];
	}
}

var zmien = function () {
	var tab = document.getElementById('tabela');
	
	tab.setAttributes({
		cellPadding: 4
	});
}
</script>
</head>
<body>
<table id="tabela" width="200"><tr><td>test</td></tr></table>
<input type="button" value="test" onclick="zmien();">
</body>
</html>

Działa na wszystkich przeglądarkach oprócz IE. Wie ktoś jak to poprawnie powinno wyglądać?

0

You're doing it wrong!

Po pierwsze, to użycie Object.prototype nie ma tu sensu jeśli setAttributes ma operować tylko na elementach DOM. Bo tak, jak masz teraz, to metodę setPrototype zyskują również... wszystkie stringi w programie. Albo wszystkie liczby. One też dziedziczą po Object.prototype! Rozumiesz chyba, że to nie ma sensu. A oprócz tego, że nie ma sensu, to jeszcze w IE nie działa, bo IE jest dziwne (powinno to działać, bo skoro wszystkie obiekty dziedziczą po Object, to wszystkie -- w IE elementy DOM najwyraźniej po Object nie dziedziczą).

Więc powinieneś użyć bardziej odpowiedniego prototypu. Najlepiej takiego, który odnosi się do wszystkich elementów DOM. Rzeczywiście, istnieje taki prototyp. Zwie się po prostu Element.prototype. Czyli piszesz:

Element.prototype = function() {
...
}

No i koniecznie dodaj DOCTYPE do strony, bo tylko wtedy będzie to działało w IE. Aha, wspominałem już, że będzie działało tylko od IE8 w górę? No, to tak właśnie będzie. Miłego dnia :/.

edit: A tak w ogóle to niektórzy nie zalecają modyfikowania prototypów obiektów wbudowanych w język, czy w przeglądarkę. Jeszcze gdy rozszerzamy DOM to pół biedy, ale gdy dorwiemy się do Object.prototype, to nagle połowa pętli for-in przestanie prawidłowo działać, bo nie są zabezpieczone przy użyciu hasOwnProperty. Według niektórych nie powinno się nawet rozszerzać elementów DOM (w jakikolwiek sposób), tylko wszystkie dane przechowywać lokalnie.

Wiesz, zawsze mógłbyś po prostu używać zamiast komorkaTabeli.setAttributes({ color: red }) to czegoś w stylu setAttributes(komorkaTabeli, { color: red });. Wymagałoby to tylko niewielkiej przeróbki funkcji anonimowej podstawianej do Object.prototype.setAttributes. Gdybyś zamknął tę nową funkcję setAtrributes w domknięciu zawierającym cały kod, to nawet nie zaśmieciłbyś przestrzeni globalnej. A jakbyś musiał zaśmiecić, to mógłbyś użyć wzorca symulującego przestrzenie nazw, czyli MYAPP.util.setAttributes(komorkaTabeli, { color: red }). Choć nie wygląda to już tak przejrzyście.

0

Istnieje też inny ciekawy sposób

Element.setAttributes = function (attrs) {
  for ( var attrName in attrs )
    this.setAttribute(attrName, attrs[attrName]);
}

Czyli tworzysz statyczną funkcję w obiekcie Element.
Następnie używasz js-owego tricku związanego z przekazaniem kontekstu do funkcji.

var elem = document.getElementById("test"),
  attributes = {cellPadding: 4};

Element.setAttributes.apply(elem, [attributes]);

alert(elem.getAttribute("cellPadding"));

Teraz obiekt this w funkcji Elem.setAttributes będzie wskazywał na zmienną elem, którą jest pobrany element. Możesz wziąć id elementu - this.id, itd. :)
Dzieje się to poprzez użycie funkcji apply, która znajduje się w prototypie każdej funkcji ( dziwnie to brzmi, wiem ;) ). Pierwszym argumentem apply jest kontekst - na co ma wskazywać this. W przypadku gdy podasz null będzie to obiekt window, w przypadku gdy jakiś zwyczajny obiekt - ten obiekt właśnie. W przykładzie wyżej obiektem był element.
Drugim argumentem fcji apply jest tablica argumentów, z jakimi ma być wywołana funkcja.

1 użytkowników online, w tym zalogowanych: 0, gości: 1