Me pregunto cuál es la mejor manera de crear un objeto JavaScript que tenga propiedades y métodos.
He visto ejemplos en los que la persona usa var self = this
y luego usa self.
en todas las funciones para asegurarse de que el alcance sea siempre correcto.
Luego he visto ejemplos de uso .prototype
para agregar propiedades, mientras que otros lo hacen en línea.
¿Alguien puede darme un ejemplo adecuado de un objeto JavaScript con algunas propiedades y métodos?
javascript
Michael Stum
fuente
fuente
self
una palabra reservada? Si no, debe ser; dado queself
es una variable predefinida que se refiere a la ventana actual.self === window
window
en el modelo de objetos del navegador comodocument
oframes
; ciertamente puede reutilizar el identificador como nombre de variable. Aunque, sí, estilísticamente prefierovar that= this
evitar cualquier posible confusión. Aunque enwindow.self
última instancia no tiene sentido, rara vez hay alguna razón para tocarlo.this
a una variable local (por ejemploself
) reduce los tamaños de archivo.Respuestas:
Hay dos modelos para implementar clases e instancias en JavaScript: la forma de creación de prototipos y la forma de cierre. Ambos tienen ventajas y desventajas, y hay muchas variaciones extendidas. Muchos programadores y bibliotecas tienen diferentes enfoques y funciones de utilidad de manejo de clases para empapelar algunas de las partes más feas del lenguaje.
El resultado es que en una empresa mixta tendrá una mezcla de metaclases, todas con un comportamiento ligeramente diferente. Lo que es peor, la mayoría del material de tutorial de JavaScript es terrible y ofrece algún tipo de compromiso intermedio para cubrir todas las bases, dejándolo muy confundido. (Probablemente, el autor también está confundido. El modelo de objetos de JavaScript es muy diferente a la mayoría de los lenguajes de programación y, en muchos lugares, está mal diseñado).
Comencemos con el prototipo . Esta es la versión más nativa de JavaScript que puede obtener: hay un mínimo de código indirecto y instancia de funcionará con instancias de este tipo de objeto.
Podemos agregar métodos a la instancia creada
new Shape
escribiéndolos en laprototype
búsqueda de esta función constructora:Ahora para subclasificarlo, en la medida en que puede llamar lo que JavaScript hace subclases. Hacemos eso reemplazando por completo esa extraña
prototype
propiedad mágica :antes de agregarle métodos:
Este ejemplo funcionará y verá un código similar en muchos tutoriales. Pero hombre, eso
new Shape()
es feo: estamos creando instancias de la clase base a pesar de que no se va a crear una Forma real. Le pasa a trabajar en este caso sencillo, porque JavaScript está tan descuidado: permite cero argumentos que se pasan en, en cuyo casox
yy
se vuelvenundefined
y se asignan al prototipo dethis.x
ythis.y
. Si la función constructora estuviera haciendo algo más complicado, se caería de bruces.Entonces, lo que debemos hacer es encontrar una forma de crear un objeto prototipo que contenga los métodos y otros miembros que queremos a nivel de clase, sin llamar a la función constructora de la clase base. Para hacer esto, vamos a tener que comenzar a escribir código auxiliar. Este es el enfoque más simple que conozco:
Esto transfiere los miembros de la clase base en su prototipo a una nueva función de constructor que no hace nada, luego usa ese constructor. Ahora podemos escribir simplemente:
en lugar de lo
new Shape()
incorrecto. Ahora tenemos un conjunto aceptable de primitivas para clases construidas.Hay algunos refinamientos y extensiones que podemos considerar bajo este modelo. Por ejemplo, aquí hay una versión sintáctica de azúcar:
Cualquiera de las versiones tiene el inconveniente de que la función de constructor no se puede heredar, como sucede en muchos idiomas. Entonces, incluso si su subclase no agrega nada al proceso de construcción, debe recordar llamar al constructor base con cualquier argumento que la base desee. Esto se puede automatizar un poco
apply
, pero aún debe escribir:Entonces, una extensión común es dividir el material de inicialización en su propia función en lugar del propio constructor. Esta función puede heredar de la base muy bien:
Ahora acabamos de obtener la misma plantilla de función de constructor para cada clase. Tal vez podamos moverlo a su propia función auxiliar para que no tengamos que seguir tipeándolo, por ejemplo, en lugar de
Function.prototype.subclass
girarlo y dejar que la función de la clase base escupe subclases:... que está empezando a parecerse un poco más a otros idiomas, aunque con una sintaxis un poco más torpe. Puede agregar algunas características adicionales si lo desea. Tal vez desee
makeSubclass
tomar y recordar un nombre de clase y proporcionar un valor predeterminado paratoString
usarlo. Tal vez desee hacer que el constructor detecte cuándo se ha llamado accidentalmente sin elnew
operador (que de lo contrario a menudo resultaría en una depuración muy molesta):Tal vez desee pasar a todos los nuevos miembros y
makeSubclass
agregarlos al prototipo, para evitar tener que escribirClass.prototype...
tanto. Muchos sistemas de clase hacen eso, por ejemplo:Hay muchas características potenciales que podría considerar deseables en un sistema de objetos y nadie está realmente de acuerdo con una fórmula en particular.
La forma de cierre , entonces. Esto evita los problemas de la herencia basada en prototipos de JavaScript, al no utilizar la herencia en absoluto. En lugar:
Ahora cada instancia de
Shape
tendrá su propia copia deltoString
método (y cualquier otro método u otro miembro de clase que agreguemos).Lo malo de que cada instancia tenga su propia copia de cada miembro de la clase es que es menos eficiente. Si se trata de un gran número de instancias subclasificadas, la herencia prototípica puede servirle mejor. Además, llamar a un método de la clase base es un poco molesto como puede ver: tenemos que recordar cuál era el método antes de que el constructor de la subclase lo sobrescribiera, o se pierde.
[También porque no hay herencia aquí, el
instanceof
operador no funcionará; Tendría que proporcionar su propio mecanismo para detectar clases si lo necesita. Si bien podría manipular los objetos prototipo de una manera similar a la de la herencia prototipo, es un poco complicado y realmente no vale la pena simplemente comenzar ainstanceof
trabajar.]Lo bueno de que cada instancia tenga su propio método es que el método puede estar vinculado a la instancia específica que lo posee. Esto es útil debido a la extraña forma de vinculación de JavaScript
this
en las llamadas a métodos, que tiene el resultado de que si desconecta un método de su propietario:luego,
this
dentro del método no estará la instancia de Circle como se esperaba (en realidad será elwindow
objeto global , causando problemas de depuración generalizados). En realidad, esto suele ocurrir cuando se toma un método y se le asigna asetTimeout
,onclick
oEventListener
en general.Con el prototipo, debe incluir un cierre para cada tarea:
o, en el futuro (o ahora si piratea Function.prototype) también puede hacerlo con
function.bind()
:si sus instancias se realizan de la manera de cierre, el enlace se realiza de forma gratuita mediante el cierre sobre la variable de instancia (generalmente se llama
that
oself
, aunque personalmente recomendaría que esta últimaself
ya tenga otro significado diferente en JavaScript). Sin1, 1
embargo, no obtienes los argumentos en el fragmento anterior de forma gratuita, por lo que aún necesitarías otro cierre o un abind()
si necesitas hacerlo.También hay muchas variantes en el método de cierre. Es posible que prefiera omitir por
this
completo, crear uno nuevothat
y devolverlo en lugar de usar elnew
operador:¿Qué camino es "correcto"? Ambos. Cuál es el mejor"? Eso depende de tu situación. FWIW Tiendo a la creación de prototipos para la herencia real de JavaScript cuando estoy haciendo mucho OO y cierres para efectos de página desechables simples.
Pero ambas formas son bastante contra intuitivas para la mayoría de los programadores. Ambos tienen muchas posibles variaciones desordenadas. Conocerá ambos (así como muchos esquemas intermedios y generalmente rotos) si utiliza el código / bibliotecas de otras personas. No hay una respuesta generalmente aceptada. Bienvenido al maravilloso mundo de los objetos JavaScript.
[Esto ha sido parte 94 de Por qué JavaScript no es mi lenguaje de programación favorito.]
fuente
new
.Utilizo este patrón con bastante frecuencia: descubrí que me da una gran cantidad de flexibilidad cuando lo necesito. En uso es bastante similar a las clases de estilo Java.
Esto utiliza una función anónima que se llama a la creación, devolviendo una nueva función de constructor. Debido a que la función anónima se llama solo una vez, puede crear variables privadas estáticas (están dentro del cierre, visibles para los otros miembros de la clase). La función de constructor es básicamente un objeto Javascript estándar: usted define atributos privados dentro de él y los atributos públicos están asociados a la
this
variable.Básicamente, este enfoque combina el enfoque crockfordiano con objetos Javascript estándar para crear una clase más poderosa.
Puede usarlo como lo haría con cualquier otro objeto Javascript:
fuente
this
¿aún necesita ser instanciadonew
?this
no acostumbrarse en laconstructor
función, que se convierteFoo
en el ejemplo.constructor.prototype.myMethod = function () { ... }
.Douglas Crockford discute ese tema extensamente en The Good Parts . Recomienda evitar que el nuevo operador cree nuevos objetos. En su lugar, propone crear constructores personalizados. Por ejemplo:
En Javascript, una función es un objeto y se puede usar para construir objetos junto con el nuevo operador. Por convención, las funciones destinadas a ser utilizadas como constructores comienzan con una letra mayúscula. A menudo ves cosas como:
En caso de que olvide usar el nuevo operador mientras crea una instancia de un nuevo objeto, lo que obtiene es una llamada a una función ordinaria, y esto está vinculado al objeto global en lugar del nuevo objeto.
fuente
new
devar that = {};
todos modos.Para continuar con la respuesta de bobince
En es6 ahora puedes crear un
class
Entonces ahora puedes hacer:
Entonces, extiéndalo a un círculo (como en la otra respuesta) que puede hacer:
Termina un poco más limpio en es6 y un poco más fácil de leer.
Aquí hay un buen ejemplo de ello en acción:
Mostrar fragmento de código
fuente
También puede hacerlo de esta manera, utilizando estructuras:
Entonces :
fuente
Otra forma sería http://jsfiddle.net/nnUY4/ (no sé si este tipo de manejo de creación de objetos y funciones reveladoras siguen algún patrón específico)
fuente
Cuando se usa el truco de cerrar "esto" durante una invocación de constructor, es para escribir una función que pueda ser utilizada como devolución de llamada por algún otro objeto que no quiera invocar un método en un objeto. No está relacionado con "hacer que el alcance sea correcto".
Aquí hay un objeto JavaScript vainilla:
Puede obtener mucho de leer lo que Douglas Crockford tiene que decir sobre JavaScript. John Resig también es brillante. ¡Buena suerte!
fuente
this
tiene que ver con "hacer que el alcance sea correcto".self=this
aunque no obligathis
a persistir, permite fácilmente el alcance "correcto" mediante un cierre.Closure
Es versátil. bobince ha resumido bien los enfoques prototipo versus cierre al crear objetos. Sin embargo, puede imitar algunos aspectos delOOP
uso del cierre de una manera funcional de programación. Recuerde que las funciones son objetos en JavaScript ; así que use la función como objeto de una manera diferente.Aquí hay un ejemplo de cierre:
Hace un tiempo me encontré con el artículo de Mozilla sobre Cierre. Esto es lo que salta a mis ojos: "Un cierre le permite asociar algunos datos (el entorno) con una función que opera en esos datos. Esto tiene paralelos obvios con la programación orientada a objetos, donde los objetos nos permiten asociar algunos datos (las propiedades del objeto ) con uno o más métodos ". Fue la primera vez que leí un paralelismo entre el cierre y la OOP clásica sin referencia al prototipo.
¿Cómo?
Suponga que desea calcular el IVA de algunos artículos. Es probable que el IVA se mantenga estable durante la vida útil de una aplicación. Una forma de hacerlo en OOP (pseudocódigo):
Básicamente, pasa un valor de IVA a su constructor y su método de cálculo puede operar sobre él a través del cierre . Ahora, en lugar de usar una clase / constructor, pase su IVA como argumento a una función. Como lo único que le interesa es el cálculo en sí, devuelve una nueva función, que es el método de cálculo:
En su proyecto, identifique los valores de nivel superior que sean buenos candidatos para el cálculo del IVA. Como regla general cada vez que pasa los mismos argumentos una y otra vez, hay una manera de mejorarlo usando el cierre. No es necesario crear objetos tradicionales.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
fuente
Creando un objeto
La forma más fácil de crear un objeto en JavaScript es usar la siguiente sintaxis:
Esto funciona muy bien para almacenar datos de forma estructurada.
Sin embargo, para casos de uso más complejos, a menudo es mejor crear instancias de funciones:
Esto le permite crear múltiples objetos que comparten el mismo "plano", similar a cómo usa las clases, por ejemplo. Java.
Sin embargo, esto todavía se puede hacer de manera más eficiente mediante el uso de un prototipo.
Siempre que diferentes instancias de una función compartan los mismos métodos o propiedades, puede moverlas al prototipo de ese objeto. De esa manera, cada instancia de una función tiene acceso a ese método o propiedad, pero no necesita duplicarse para cada instancia.
En nuestro caso, tiene sentido mover el método
f
al prototipo:Herencia
Una manera simple pero efectiva de hacer herencia en JavaScript es utilizar el siguiente dos líneas:
Eso es similar a hacer esto:
La principal diferencia entre ambos es que el constructor de
A
no se ejecuta cuando se usaObject.create
, que es más intuitivo y más similar a la herencia basada en clases.Siempre puede optar por ejecutar opcionalmente el constructor de
A
al crear una nueva instancia deB
agregando agregándolo al constructor deB
:Si desea pasar todos los argumentos de
B
aA
, también puede usarFunction.prototype.apply()
:Si desea mezclar otro objeto en la cadena del constructor
B
, puede combinarloObject.create
conObject.assign
:Manifestación
Nota
Object.create
se puede usar de forma segura en todos los navegadores modernos, incluido IE9 +.Object.assign
no funciona en ninguna versión de IE ni en algunos navegadores móviles. Se recomienda rellenar polivinílicamenteObject.create
y / oObject.assign
si desea utilizarlos y admitir navegadores que no los implementen.Puede encontrar un polyfill por
Object.create
aquí y uno porObject.assign
aquí .fuente
fuente
Además de la respuesta aceptada de 2009. Si puede dirigirse a los navegadores modernos, puede utilizar Object.defineProperty .
Para ver una lista de compatibilidad de escritorio y móvil, consulte la lista de compatibilidad del navegador de Mozilla . Sí, IE9 + lo admite, así como Safari mobile.
fuente
también puedes probar esto
fuente
Primero, puede cambiar su preferencia de agregar métodos a la instancia en lugar del
prototype
objeto del constructor . Casi siempre declaro métodos dentro del constructor porque uso el secuestro de constructores muy a menudo para propósitos de herencia y decoradores.Así es como decido dónde se escriben las declaraciones:
this
)var
declaraciones tengan prioridad sobre lasfunction
declaraciones{}
y[]
)public
declaraciones tengan prioridad sobre lasprivate
declaracionesFunction.prototype.bind
másthus
,self
,vm
,etc
this
desde el alcance léxico del espacio de cierre.He aquí por qué estos ayudan:
Secuestro de constructores Diseño del modelo Patrones de diseñoComo puede ver, realmente no lo necesito
thus
ya que prefieroFunction.prototype.bind
(.call
o.apply
) sobrethus
. En nuestraSingleton
clase, ni siquiera lo nombramosthus
porqueINSTANCE
transmite más información. ParaModel
, regresamosthis
para poder invocar al Constructor usando.call
para devolver la instancia que le pasamos. Redundantemente, lo asignamos a la variableupdated
, aunque es útil en otros escenarios.Además, prefiero construir literales de objeto usando la
Privilegiado No preferidonew
palabra clave sobre {corchetes}:Como puede ver, este último no tiene la capacidad de anular la propiedad de "anulación" de su Superclase.
Si agrego métodos al
Privilegiado No preferidoprototype
objeto de la Clase , prefiero un literal de objeto, con o sin usar lanew
palabra clave:fuente
Me gustaría mencionar que podemos usar un Título o una Cadena para declarar un Objeto.
Hay diferentes formas de llamar a cada tipo de ellos. Vea abajo:
fuente
Básicamente, no hay un concepto de clase en JS, por lo que utilizamos la función como un constructor de clase que es relevante con los patrones de diseño existentes.
Hasta ahora, JS no tiene idea de que desea crear un objeto, así que aquí viene la nueva palabra clave.
Ref: JS profesional para desarrolladores web - Nik Z
fuente
class
en JS, como usted mencionó en su encabezado utilizando lafunction
palabra clave. Es no un patrón de diseño, pero una característica intencional de la lengua. No te rechacé por esto, pero parece que alguien más lo hizo por la brevedad y la casi irrelevancia de la pregunta. Espero que este comentario ayude.