Quiero escribir algunas clases de Javascript que extiendan los nodos DOM (para poder insertar instancias de mi clase directamente en el DOM), pero tengo dificultades para descubrir de qué clase / prototipo debería heredar.

P.ej.:

function myExtendedElement() {
       this.superclass = ClassA;
       this.superclass();
       delete this.superclass;
} 

Pero, ¿qué debería ser la clase A?

7
wheresrhys 29 sep. 2009 a las 02:47

4 respuestas

La mejor respuesta

no es una buena idea hacer esto.

En primer lugar, para heredar del elemento DOM, debe tener acceso al prototipo de ese elemento. El problema es que no todos los navegadores proporcionan acceso a prototipos de elementos DOM . Los clientes más nuevos basados en Gecko y WebKit, por ejemplo, exponen algunos de estos prototipos como objetos globales: HTMLDivElement, HTMLElement, Element, Node, etc.

Por ejemplo, el elemento DIV simple generalmente tiene una cadena prototipo similar a:

HTMLDivElement.prototype -> HTMLElement.prototype -> Element.prototype 
  -> Node.prototype -> Object.prototype -> null

Puede acceder a cualquiera de ellos y ampliar o heredar de la forma deseada. Pero, una vez más, aunque pueda, le recomiendo no hacerlo .

Cuando el navegador no expone estos prototipos, estás sin suerte . Puede intentar recuperarlos siguiendo la propiedad constructor del elemento DOM mismo:

document.createElement('div').constructor;

- pero no hay garantía de que el elemento tenga la propiedad constructor (por ejemplo, IE6 no) e incluso si lo tiene, esta propiedad hace referencia al objeto "correcto". Si, después de todo, el constructor hace referencia al objeto correcto, todavía no hay garantía de que se permita aumentar estos objetos . La verdad es que los objetos host pueden implementar un comportamiento completamente extraño y ni siquiera tienen que seguir las reglas que siguen los objetos JS nativos (puede encontrar docenas de ejemplos de este tipo en la vida real).

La segunda razón por la que desea evitar heredar de los prototipos de elementos DOM es que el mecanismo de dicha herencia no se especifica realmente en ningún lugar; podría ser peculiar, impredecible y en general frágil y poco confiable.

Sí, puede crear un constructor que inicialice los objetos con la cadena de prototipo adecuada (es decir, que tenga un prototipo DOM):

function MyDivElement(){}
MyDivElement.prototype = HTMLDivElement.prototype;

var myDiv = new MyDivElement();
typeof myDiv.appendChild; // "function"

- pero esto es todo lo posible, y la utilidad de todo este enfoque se ve limitada al tener ciertos métodos en prototipo y nada más -

typeof myDivElement.nodeName; // "undefined"
myDivElement.innerHTML = '<span>foo<\/span>';
myDivElement.childNodes; // Error

Hasta que algún estándar especifique el mecanismo exacto para heredar de los prototipos DOM (y los navegadores realmente implementen ese mecanismo), es mejor dejarlos solos y tal vez intente un enfoque alternativo , p. patrones de envoltura o decorador en lugar del prototipo uno :)

20
kangax 29 sep. 2009 a las 18:57

He encontrado un truco que funciona ... al menos, me permite acceder a las propiedades del objeto extendido a través del elemento DOM y viceversa. Pero no es elegante.

var DOMelement = document.getElementById('myID'); // or  $('#myID')[0]; in jQuery
DOMelement.extended = new extensionClass(this);

function extensionClass(element) {
    this.element = element;
    ...
}
0
wheresrhys 29 sep. 2009 a las 09:13

Simplemente puede agregar nuevas funciones a los prototipos DOM, por ejemplo.

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

Entonces myNameSpaceSomeFunction existirá en todos los elementos.

1
olliej 29 sep. 2009 a las 00:48

Antigua Q, pero hay una mejor respuesta que "Hacer" o "No" ahora que IE6 está prácticamente inactivo. En primer lugar, la creación de prototipos de constructores de herencia de punto final de ECMA como 'Array' es bastante inofensivo y útil si lo hace correctamente y prueba para evitar romper los métodos existentes. Sin embargo, definitivamente mantente alejado de Object y piensa mucho antes de jugar con Function.

Sin embargo, si está compartiendo código entre muchas personas / autores, o está lidiando con la incertidumbre del DOM, generalmente es mejor crear objetos adaptadores / envoltorios con un nuevo método de fábrica para usar en un esquema de herencia.

En este caso, escribí document.createExtEl para crear elementos DOM empaquetados cuyas propiedades accesibles están disponibles a través de un prototipo.

Usando lo siguiente, su "superclase" para divs sería HTMLExtDivElement (en este caso disponible globalmente - ew, pero es solo un ejemplo). Todas las referencias a las propiedades disponibles de la instancia HTMLElement original viven dentro del prototipo del contenedor. Nota: algunas propiedades antiguas de IE no se pueden pasar como referencias o incluso acceder sin arrojar errores (impresionante), que es para lo que sirve el try / catch.

Puede normalizar las propiedades comunes agregando lógica para colocar las propiedades faltantes o estandarizadas justo después de que el bucle envuelva las propiedades disponibles para la instancia, pero se lo dejaré a usted.

Ahora, por el amor de Pete, nunca use mi código para escribir una tontería de herencia 16 veces en cascada y luego implementarlo en una biblioteca irónicamente popular con la que todos estamos obligados a lidiar o lo buscaré y citaré en voz alta "Diseño Patrones "hacia ti mientras arrojas fruta podrida.

//Implementation just like document.createElement()
//document.createExtEl('div').tagName === 'DIV'

document.createExtEl = ( function(){  //returns a function below

            var htmlTags = ['div','a'], //... add all the element tags you care to make extendable here
            constructorMap = {},
            i = htmlTags.length;

            while(i--){
                thisTag = htmlTags[i].toLowerCase();
                constructorMap[ thisTag ] = function(){
                    var elObj = document.createElement(thisTag),
                    thisProto = this.constructor.prototype,
                    constructorName = 'HTMLExt' + thisTag.charAt(0).toUpperCase() + thisTag.slice(1) + 'Element';

                    alert(constructorName);

                    window[constructorName] = this.constructor; //adds a global reference you can access the new constructor from.

                    for(var x in elObj){ try{ thisProto[x] = elObj[x]; } catch(e){} }
                }
            }

            //all of the above  executes once and returned function accesses via closure
            return function(tagName){
                return new constructorMap[tagName.toLowerCase()]();
            }



    } )()

    //Now in the case of a superclass/constructor for div, you could use HTMLExtDivElement globally
3
Erik Reppen 22 may. 2012 a las 22:16