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 = thisy luego usa self.en todas las funciones para asegurarse de que el alcance sea siempre correcto.
Luego he visto ejemplos de uso .prototypepara 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

selfuna palabra reservada? Si no, debe ser; dado queselfes una variable predefinida que se refiere a la ventana actual.self === windowwindowen el modelo de objetos del navegador comodocumentoframes; ciertamente puede reutilizar el identificador como nombre de variable. Aunque, sí, estilísticamente prefierovar that= thisevitar cualquier posible confusión. Aunque enwindow.selfúltima instancia no tiene sentido, rara vez hay alguna razón para tocarlo.thisa 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 Shapeescribiéndolos en laprototypebú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
prototypepropiedad 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 casoxyyse vuelvenundefinedy se asignan al prototipo dethis.xythis.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.subclassgirarlo 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
makeSubclasstomar y recordar un nombre de clase y proporcionar un valor predeterminado paratoStringusarlo. Tal vez desee hacer que el constructor detecte cuándo se ha llamado accidentalmente sin elnewoperador (que de lo contrario a menudo resultaría en una depuración muy molesta):Tal vez desee pasar a todos los nuevos miembros y
makeSubclassagregarlos 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
Shapetendrá su propia copia deltoStringmé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
instanceofoperador 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 ainstanceoftrabajar.]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
thisen las llamadas a métodos, que tiene el resultado de que si desconecta un método de su propietario:luego,
thisdentro del método no estará la instancia de Circle como se esperaba (en realidad será elwindowobjeto global , causando problemas de depuración generalizados). En realidad, esto suele ocurrir cuando se toma un método y se le asigna asetTimeout,onclickoEventListeneren 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
thatoself, aunque personalmente recomendaría que esta últimaselfya tenga otro significado diferente en JavaScript). Sin1, 1embargo, 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
thiscompleto, crear uno nuevothaty devolverlo en lugar de usar elnewoperador:¿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
thisvariable.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?thisno acostumbrarse en laconstructorfunción, que se convierteFooen 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
newdevar that = {};todos modos.Para continuar con la respuesta de bobince
En es6 ahora puedes crear un
classEntonces 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
thistiene que ver con "hacer que el alcance sea correcto".self=thisaunque no obligathisa persistir, permite fácilmente el alcance "correcto" mediante un cierre.ClosureEs versátil. bobince ha resumido bien los enfoques prototipo versus cierre al crear objetos. Sin embargo, puede imitar algunos aspectos delOOPuso 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
fal 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
Ano 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
Aal crear una nueva instancia deBagregando agregándolo al constructor deB:Si desea pasar todos los argumentos de
BaA, también puede usarFunction.prototype.apply():Si desea mezclar otro objeto en la cadena del constructor
B, puede combinarloObject.createconObject.assign:Manifestación
Nota
Object.createse puede usar de forma segura en todos los navegadores modernos, incluido IE9 +.Object.assignno funciona en ninguna versión de IE ni en algunos navegadores móviles. Se recomienda rellenar polivinílicamenteObject.createy / oObject.assignsi desea utilizarlos y admitir navegadores que no los implementen.Puede encontrar un polyfill por
Object.createaquí y uno porObject.assignaquí .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
prototypeobjeto 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)vardeclaraciones tengan prioridad sobre lasfunctiondeclaraciones{}y[])publicdeclaraciones tengan prioridad sobre lasprivatedeclaracionesFunction.prototype.bindmásthus,self,vm,etcthisdesde 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
thusya que prefieroFunction.prototype.bind(.callo.apply) sobrethus. En nuestraSingletonclase, ni siquiera lo nombramosthusporqueINSTANCEtransmite más información. ParaModel, regresamosthispara poder invocar al Constructor usando.callpara 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 preferidonewpalabra 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 preferidoprototypeobjeto de la Clase , prefiero un literal de objeto, con o sin usar lanewpalabra 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
classen JS, como usted mencionó en su encabezado utilizando lafunctionpalabra 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.