Supongamos que tengo un objeto foo
en mi código JavaScript. foo
es un objeto complejo y se genera en otro lugar. ¿Cómo puedo cambiar el prototipo del foo
objeto?
Mi motivación es establecer prototipos apropiados para objetos serializados de .NET a literales de JavaScript.
Suponga que he escrito el siguiente código JavaScript dentro de una página ASP.NET.
var foo = <%=MyData %>;
Supongamos que MyData
es el resultado de invocar .NET JavaScriptSerializer
en un Dictionary<string,string>
objeto.
En tiempo de ejecución, esto se convierte en lo siguiente:
var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}];
Como puede ver, se foo
convierte en una matriz de objetos. Me gustaría poder inicializar foo
con un prototipo apropiado. Yo no quiero modificar el Object.prototype
ni Array.prototype
. ¿Cómo puedo hacer esto?
javascript
function-prototypes
Río vivian
fuente
fuente
extend
o Googlegoog.inherit
? Muchos desarrolladores proporcionan formas de generar herencia antes de llamar alnew
constructor de edad avanzada , que es antes de que nos lo dieranObject.create
y no teníamos que preocuparnos por anularObject.prototype
.Respuestas:
EDITAR Febrero de 2012: la respuesta a continuación ya no es precisa. __proto__ se está agregando a ECMAScript 6 como "normativo opcional", lo que significa que no se requiere que se implemente, pero si lo está, debe seguir el conjunto de reglas dado. Esto no está resuelto actualmente pero al menos será oficialmente parte de la especificación de JavaScript.
Esta pregunta es mucho más complicada de lo que parece en la superficie, y más allá del nivel de pago de la mayoría de las personas en lo que respecta al conocimiento de los componentes internos de Javascript.
La
prototype
propiedad de un objeto se utiliza al crear nuevos objetos secundarios de ese objeto. Cambiarlo no se refleja en el objeto en sí, sino que se refleja cuando ese objeto se utiliza como constructor de otros objetos y no tiene ninguna utilidad para cambiar el prototipo de un objeto existente.Los objetos tienen una propiedad [[prototype]] interna que apunta al prototipo actual. La forma en que funciona es cada vez que se llama a una propiedad en un objeto, comenzará en el objeto y luego irá hacia arriba a través de la cadena [[prototype]] hasta que encuentre una coincidencia, o falle, después del prototipo del objeto raíz. Así es como Javascript permite la construcción y modificación de objetos en tiempo de ejecución; tiene un plan para buscar lo que necesita.
La
__proto__
propiedad existe en algunas implementaciones (muchas ahora): cualquier implementación de Mozilla, todas las webkit que conozco, algunas otras. Esta propiedad apunta a la propiedad interna [[prototype]] y permite la modificación posterior a la creación en los objetos. Todas las propiedades y funciones cambiarán instantáneamente para coincidir con el prototipo debido a esta búsqueda encadenada.Esta característica, aunque está estandarizada ahora, todavía no es una parte requerida de JavaScript, y en los lenguajes que la soportan tiene una alta probabilidad de derribar su código en la categoría "no optimizado". Los motores JS deben hacer todo lo posible para clasificar el código, especialmente el código "activo" al que se accede con mucha frecuencia, y si está haciendo algo elegante como modificar
__proto__
, no optimizarán su código en absoluto.Esta publicación https://bugzilla.mozilla.org/show_bug.cgi?id=607863 analiza específicamente las implementaciones actuales
__proto__
y las diferencias entre ellas. Cada implementación lo hace de manera diferente, porque es un problema difícil y sin resolver. Todo en Javascript es mutable, excepto a.) La sintaxis b.) Objetos de host (el DOM existe técnicamente fuera de Javascript) y c.)__proto__
. El resto está completamente en manos de usted y de cualquier otro desarrollador, por lo que puede ver por qué__proto__
sobresale como un pulgar dolorido.Hay una cosa que
__proto__
permite que de otro modo sea imposible de hacer: la designación de un prototipo de objetos en tiempo de ejecución separado de su constructor. Este es un caso de uso importante y es una de las razones principales por las__proto__
que aún no está muerto. Es bastante importante que haya sido un punto de discusión serio en la formulación de Harmony, o que pronto se conocerá como ECMAScript 6. La capacidad de especificar el prototipo de un objeto durante la creación será parte de la próxima versión de Javascript y esto será la campana que indica__proto__
los días está formalmente numerada.A corto plazo, puede usarlo
__proto__
si se dirige a navegadores que lo admitan (no IE, y ningún IE lo hará). Es probable que funcione en webkit y moz durante los próximos 10 años, ya que ES6 no se finalizará hasta 2013.Brendan Eich - re: Enfoque de nuevos métodos de objetos en ES5 :
fuente
non-writable, configurable
solucionaría estas preocupaciones al obligar al usuario a reconfigurar explícitamente la propiedad. Al final, las malas prácticas están asociadas con los abusos de las capacidades de un idioma, no con las capacidades en sí mismas. __Proto__ escribible no es inaudito. Hay muchas otras propiedades de escritura no enumerables y, si bien existen peligros, también existen las mejores prácticas. La cabeza de un martillo no debe quitarse simplemente porque se puede usar de manera incorrecta para herir a alguien.__proto__
es necesaria, porque Object.create solo producirá Objetos, no funciones, por ejemplo, ni Símbolos, Regexes, elementos DOM u otros objetos host. Si desea que sus objetos sean invocables o especiales de otras maneras, pero aún así cambian su cadena de prototipos, está atascado sin configurables__proto__
o Proxies__proto__
preocupación " en JSON". Las otras preocupaciones de Brendan Eich son ridículas. Las cadenas de prototipos recursivas o el uso de un prototipo con métodos inadecuados para el objeto dado son errores del programador, no fallas de lenguaje y no deberían ser un factor, y además pueden ocurrir con Object.create tan bien como con libremente configurable__proto__
.__proto__
.ES6 finalmente especifica Object.setPrototypeOf (objeto, prototipo) que ya está implementado en Chrome y Firefox.
fuente
Puedes usar
constructor
en una instancia de un objeto para alterar el prototipo de un objeto in situ. Creo que esto es lo que estás pidiendo hacer.Esto significa que si tiene
foo
cuál es una instancia deFoo
:Puede agregar una propiedad
bar
a todas las instancias deFoo
haciendo lo siguiente:Aquí hay un violín que muestra la prueba de concepto: http://jsfiddle.net/C2cpw/ . No estoy muy seguro de cómo les irá a los navegadores más antiguos con este enfoque, pero estoy bastante seguro de que esto debería funcionar bastante bien.
Si su intención es mezclar la funcionalidad en objetos, este fragmento debería hacer el trabajo:
fuente
foo.__proto__.bar = 'bar';
Puede hacerlo
foo.__proto__ = FooClass.prototype
, AFAIK que es compatible con Firefox, Chrome y Safari. Tenga en cuenta que la__proto__
propiedad no es estándar y podría desaparecer en algún momento.Documentación: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto . Consulte también http://www.mail-archive.com/[email protected]/msg00392.html para obtener una explicación de por qué no existe
Object.setPrototypeOf()
y por qué__proto__
está obsoleto.fuente
Puede definir su función de constructor de proxy y luego crear una nueva instancia y copiar todas las propiedades del objeto original en ella.
Demo en vivo: http://jsfiddle.net/6Xq3P/
El
Custom
constructor representa el nuevo prototipo, ergo, suCustom.prototype
objeto contiene todas las nuevas propiedades que le gustaría usar con su objeto original.Dentro del
Custom
constructor, simplemente copie todas las propiedades del objeto original al nuevo objeto de instancia.Este nuevo objeto de instancia contiene todas las propiedades del objeto original (se copiaron en él dentro del constructor), y también todas las nuevas propiedades definidas adentro
Custom.prototype
(porque el nuevo objeto es unaCustom
instancia).fuente
No puede cambiar el prototipo de un objeto JavaScript que ya ha sido instanciado en una forma de navegador cruzado. Como han mencionado otros, sus opciones incluyen:
__proto__
propiedad de navegador cruzado / no estándarNinguno de los dos es particularmente bueno, especialmente si tiene que recorrer recursivamente un objeto hacia los objetos internos para cambiar efectivamente el prototipo completo de un elemento.
Solución alternativa a la pregunta
Voy a echar un vistazo más abstracto a la funcionalidad que parece que deseas.
Básicamente, los prototipos / métodos solo permiten una forma de agrupar funciones basadas en un objeto.
En lugar de escribir
usted escribe
La sintaxis anterior se ha acuñado con el término OOP debido a la sintaxis object.method (). Algunas de las principales ventajas de OOP sobre la programación funcional tradicional incluyen:
obj.replace('needle','replaced')
frente a tener que recordar nombres comostr_replace ( 'foo' , 'bar' , 'subject')
y la ubicación de las diferentes variablesstring.trim().split().join()
) es potencialmente más fácil de modificar y escribir que funciones anidadasjoin(split(trim(string))
Desafortunadamente, en JavaScript (como se muestra arriba) no se puede modificar un prototipo ya existente. Idealmente arriba, podría modificar
Object.prototype
solo para el Objeto dado arriba, pero desafortunadamente modificarObject.prototype
podría romper los scripts (lo que resultaría en colisión de propiedades y anulación).No existe un término medio de uso común entre estos 2 estilos de programación, ni una forma de programación orientada a objetos para organizar funciones personalizadas.
UnlimitJS proporciona un término medio que le permite definir métodos personalizados. Evita:
Usando su código anterior, simplemente crearía un espacio de nombres de funciones que pretende llamar contra el objeto.
Aquí hay un ejemplo:
Puede leer más ejemplos aquí UnlimitJS . Básicamente, cuando llama
[Unlimit]()
a una función, permite que la función se llame como un método en un objeto. Es como un término medio entre la POO y las carreteras funcionales.fuente
No puede cambiar la
[[prototype]]
referencia de objetos ya construidos, hasta donde yo sé. Podría alterar la propiedad del prototipo de la función de constructor original pero, como ya ha comentado, ese constructor lo esObject
, y alterar las construcciones centrales de JS es algo malo.Sin embargo, puede crear un objeto proxy del objeto construido que implemente la funcionalidad adicional que necesita. También puede parchear los métodos y comportamientos adicionales asignándolos directamente al objeto en cuestión.
Tal vez puedas conseguir lo que quieres de otra manera, si estás dispuesto a abordar desde un ángulo diferente: ¿Qué necesitas hacer que implique jugar con el prototipo?
fuente
__proto__
propiedad. Esto solo funcionará en navegadores que admitan la__proto__
notación, que es Chrome y Firefox, y ha quedado obsoleto . Entonces, en resumen, puede cambiar el[[prototype]]
de un objeto, pero probablemente no debería hacerlo.Si conoce el prototipo, ¿por qué no inyectarlo en el código?
Entonces, una vez que se serializan los datos, obtienes
ahora solo necesita un constructor que tome una matriz como argumento.
fuente
No hay forma de heredar realmente de
Array
o "subclasificarlo".Lo que puede hacer es esto ( ADVERTENCIA: CÓDIGO COMPLETO ADELANTE ):
Esto funciona, pero causará ciertos problemas a cualquiera que se cruce en su camino (parece una matriz, pero las cosas saldrán mal si intenta manipularlo).
¿Por qué no sigue la ruta sensata y agrega métodos / propiedades directamente
foo
, o usa un constructor y guarda su matriz como una propiedad?fuente
Si desea crear un prototipo sobre la marcha, esta es una de las formas
fuente
fuente
prototype
es estático? No estoy seguro de lo que quieres decir.prototype
es una propiedad de una función, no un objeto.Object.prototype
existe;{}.prototype
no lo hace.Object.getPrototypeOf(foo)
para obtener el objeto prototipo del constructor. Puede modificar las propiedades para modificar el prototipo del objeto. Solo funciona en navegadores recientes, sin embargo, no podría decirte cuáles.Object.prototype
directamente porque es muy probable que eso sea loObject.getPrototypeOf(foo)
que regrese. No es muy útil.