2010-09-28 9 views
24

Durante mucho tiempo he estado considerando la idea de hacer que mi JavaScript esté más orientado a objetos. He visto algunas implementaciones diferentes de esto también, pero no puedo decidir si es necesario o no.¿Es segura la producción de la implementación OO JavaScript de John Resig?

Lo que estoy tratando de responder a las siguientes preguntas son

  • John Resig's ¿Es sencilla estructura de herencia seguro de usar para la producción?
  • ¿Hay alguna manera de poder decir qué tan bien se ha probado?
  • Además de Joose ¿qué otras opciones tengo para este propósito? Necesito uno que sea fácil de usar, rápido y robusto. También debe ser compatible con jQuery.
+22

que he visto Javascript "en producción "que hace llorar a los ángeles bebé gatito, por lo que depende de ti decidir el impacto que tendría en tu unidad/página de prueba. Es algo tan simple que es difícil imaginar que provoque problemas serios. – Pointy

+0

+1 Puntiagudo. Acabo de criticar a continuación, pero en realidad el 99% de los sitios web tienen un código más cuestionable que el de ellos ya ... – bobince

+1

jQuery para empezar ... –

Respuesta

19

Huh. Parece mucho más complicado de lo que necesita ser, para mí.

En realidad, al observar más de cerca realmente me tomo una excepción con lo que está haciendo al proporcionar this._super() mientras estoy en un método, para llamar al método de la superclase.

El código introduce una dependencia de typeof==='function' (no fiable para algunos objetos), Function#toString (argh, la descomposición función también es poco fiable), y decidir si se debe envolver en función de si usted ha utilizado la secuencia de bytes _super en el cuerpo de la función (incluso si solo lo ha usado en una cadena. Si lo intenta, por ejemplo, this['_'+'super'], fallará).

Y si está almacenando propiedades en los objetos de su función (por ejemplo, MyClass.myFunction.SOME_PRIVATE_CONSTANT, lo que podría hacer para mantener limpios los espacios de nombres), la envoltura le impedirá acceder a esas propiedades. Y si se arroja una excepción en un método y se atrapa en otro método del mismo objeto, _super terminará señalando algo incorrecto.

Todo esto es solo para hacer que llamar al método del mismo nombre de la superclase sea más fácil. Pero no creo que eso sea especialmente difícil de hacer en JS de todos modos. Es demasiado inteligente para su propio bien, y en el proceso hace que todo sea menos confiable. (Ah, y arguments.callee no es válido en el modo estricto, aunque eso no es realmente su culpa ya que ocurrió después de que lo publicó).

Esto es lo que estoy usando para las clases en este momento. No afirmo que este es el "mejor" sistema de clases JS, porque hay cargas de diferentes maneras de hacerlo y un montón de características diferentes que puede querer agregar o no agregar. Pero es muy ligero y pretende ser 'JavaScriptic', si es una palabra. (No lo es.)

Function.prototype.makeSubclass= function() { 
    function Class() { 
     if (!(this instanceof Class)) 
      throw 'Constructor function requires new operator'; 
     if ('_init' in this) 
      this._init.apply(this, arguments); 
    } 
    if (this!==Object) { 
     Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype; 
     Class.prototype= new Function.prototype.makeSubclass.nonconstructor(); 
    } 
    return Class; 
}; 
Function.prototype.makeSubclass.nonconstructor= function() {}; 

Proporciona:

  1. protección contra la falta accidental new. La alternativa es redirigir silenciosamente X() a new X() para que falte new. Es un lanzamiento que es mejor; Fui en busca de un error explícito para que uno no se acostumbre a escribir sin new y cause problemas en otros objetos no definidos así. De cualquier forma, es mejor que el inaceptable error JS de dejar que las propiedades this. caigan en window y misteriosamente se equivocan más tarde.

  2. método de _init heredable, por lo que no tiene que escribir una función de constructor que no haga más que llamar a la función de constructor de superclase.

y eso es todo.

Así es como usted puede utilizarlo para poner en práctica el ejemplo de Resig:

var Person= Object.makeSubclass(); 
Person.prototype._init= function(isDancing) { 
    this.dancing= isDancing; 
}; 
Person.prototype.dance= function() { 
    return this.dancing; 
}; 

var Ninja = Person.makeSubclass(); 
Ninja.prototype._init= function() { 
    Person.prototype._init.call(this, false); 
}; 
Ninja.prototype.swingSword= function() { 
    return true; 
}; 

var p= new Person(true); 
p.dance(); // => true 

var n = new Ninja(); 
n.dance(); // => false 
n.swingSword(); // => true 

// Should all be true 
p instanceof Person && 
n instanceof Ninja && n instanceof Person 

superclase-llamada se hace nombrando específicamente el método que desea y call ing ella, un poco como en Python. Usted podría agregar un miembro _super a la función constructora si desea evitar nombrar de nuevo Person (por lo que diría Ninja._super.prototype._init.call, o quizás Ninja._base._init.call).

+0

'typeof' no es solo confiable para funciones si se trata de objetos host, y no le gustaría subclasificar uno de esos. O al menos, no deberías querer. Ciertamente, estoy de acuerdo en que el código de Resig es demasiado truculento y frágil, y simplemente no es necesario. –

+0

@Tim: Sí, no se puede crear un prototipo de un objeto host, pero lo que está haciendo la prueba de función es verificar cada uno de los miembros, en lugar del objeto de la superclase. Por lo tanto, un objeto host que sea miembro de su clase puede causar problemas. (Por ejemplo, en Firefox 3.0 una expresión regular aparece como función, por lo que el ajuste en Class.extend podría contener a los miembros de la expresión regular al tratar de envolverlos como funciones). – bobince

+0

Oh, siempre me olvido de lo de la expresión regular. Y sí, tienes razón; No había leído el código del señor Resig tan bien como podría haberlo hecho, y ahora se ve aún más frágil que antes. Buen uso de la palabra "nadger", por cierto. Está infrautilizado en SO. –

5

JavaScript está basado en prototipos y no está basado en clases. Mi recomendación no es para luchar contra ella y declarar subtipos de la forma JS:

MyDerivedObj.prototype = new MySuperObj(); 
MyDerivedObj.prototype.constructor = MyDerivedObj; 
+3

El problema con esto es que al llamar 'new MySuperObj()' invoca el código de inicialización en el función de constructor para 'MySuperObj()', como si realmente estuviera creando una instancia de 'MySuperObj'. Tan pronto como tenga algo no trivial en la función constructora, esto no funcionará, ya que necesita invocar el código de inicialización 'MySuperObj' cuando construye una instancia' MyDerivedObj', no en definir-tiempo. – bobince

+1

Estoy de acuerdo en que es posible que desee factorizar su código de inicialización en un método diferente y llamarlo desde el constructor de MyDerivedObj ... mejor aún, considere usar Dependency Injection. Descubrí que esto reduce mucho el código de constructor. –

5

ver hasta dónde se puede obtener sin necesidad de utilizar la herencia en absoluto. Trátelo como un truco de rendimiento (que se aplicará de mala gana cuando sea realmente necesario) en lugar de un principio de diseño.

En un lenguaje altamente dinámico como JS, rara vez es necesario saber si un objeto esPerson. Solo necesita saber si tiene una propiedadfirstName o un método eatFood. A menudo no necesita saber si un objeto es una matriz; si tiene una propiedad de longitud y algunas otras propiedades con el nombre de números enteros, generalmente es lo suficientemente bueno (por ejemplo, el objeto Arguments). "Si camina como un pato y grazna como un pato, es un pato".

// give back a duck 
return { 
    walk: function() { ... }, 
    quack: function() { ... } 
}; 

Sí, si usted está haciendo un gran número de pequeños objetos, cada uno con docenas de métodos, a continuación, por todos los medios asignar esos métodos al prototipo para evitar la sobrecarga de crear docenas de ranuras en cada caso. Pero trátenlo como una forma de reducir la sobrecarga de memoria: una mera optimización. Y haga un favor a sus usuarios al ocultar el uso de new detrás de algún tipo de función de fábrica, para que ni siquiera necesiten saber cómo se crea el objeto. Solo necesitan saber que tiene el método foo o la propiedad bar.

(y tenga en cuenta que no será realmente modelando la herencia clásica en ese escenario. Es simplemente el equivalente a la definición de una sola clase para obtener la eficiencia de un vtable compartida.)

Cuestiones relacionadas