variable usar dinamicas change attribute javascript function oop object custom-properties

javascript - usar - Agregar propiedades personalizadas a una función



title html css (8)

La búsqueda de una respuesta adecuada resultó difícil debido a la existencia de muchos otros problemas relacionados con mis palabras clave, por lo que le preguntaré aquí.

Como sabemos, las funciones en javascript son objetos y tienen sus propias propiedades y métodos (más propiamente, funciones istances, heredadas de Function.prototype).

Estaba considerando agregar propiedades personalizadas para una función (método), vamos a omitir el "¿por qué?" parte e ir directamente al código:

var something = { myMethod: function () { if (something.myMethod.someProperty === undefined) { something.myMethod.someProperty = "test"; } console.log(something.myMethod); } }

Cuando se inspecciona con Firebug DOM Explorer, la propiedad se define como se espera. Sin embargo, como no me considero un experto en JavaScript, tengo las siguientes preguntas:

  1. ¿Se puede considerar este método como "adecuado" y compatible con los estándares? Funciona en Firefox, pero hay muchas cosas que funcionan como se espera en los navegadores web y de ninguna manera son estándares.
  2. ¿Es este tipo de alteración de objetos al agregarles nuevas propiedades una buena práctica?

"necromancing" aquí, pero creo que toda gran pregunta necesita respuestas simples:

y Sí *

Al asociar las propiedades a la función, se limpia el alcance, se mejora la legibilidad y se agrega cohesión lógica. Un beneficio adicional es que documenta la relación entre la función y las variables. Creo que es un diseño superior, mucho mejor que agregar variables en el alcance

Creé algunos ejemplos divertidos aquí y aquí. HERE Y AQUÍ

* Creo que vale la pena señalar que probablemente no veas esto muy a menudo. la mayoría de los desarrolladores probablemente no se dan cuenta de que es posible. Algunas personas están locas por cada gota de rendimiento ... "Los motores de JavaScript optimizan en función de la ''forma'' de un objeto ''..." blah blah blah ... ut Creo que puedes seguir la regla que tienes para Objects y para ti lo haré bien


Acepto que esta es una pregunta difícil que podría tener múltiples respuestas, por lo que prefiero hacer un ejemplo diferente:

Supongamos que tenemos una Array JavaScript, poblada por un generador:

var arr = [...new Array(10).keys()];

es decir

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Ahora queremos asignar esto a una nueva matriz: la misma longitud, aplicando alguna función, para poder usar la propiedad de función de map nativa:

arr = arr.map((value,index) => ++value)

Acabamos de hacer un value=value+1 y return, por lo que ahora la matriz se verá como

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Ok, ahora se supone que tiene un Object JavaScript como

var obj=new Object()

que se definió como la matriz anterior (por alguna razón loca):

arr.forEach((value,index) => obj[value]=value)

es decir

{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}

En este punto no podemos aplicar el mismo método de map ya que no está definido para un Object por lo que debemos definirlo como un nuevo prototype de un Object :

Object.defineProperty(Object.prototype, ''mapObject'', { value: function(f, ctx) { ctx = ctx || this; var self = this, result = {}; Object.keys(self).forEach(function(k) { result[k] = f.call(ctx, self[k], k, self); }); return result; } });

En este punto podríamos hacer lo mismo con la matriz antes:

obj=obj.mapObject((value,key) => ++value )

para que tengamos:

{0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10}

Puede ver que hemos actualizado solo los valores:

[...Object.keys(obj)] ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

y podemos volver luego a la matriz de salida:

[...Object.keys(obj).map(k=>obj[k])] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Aquí está en el trabajo:

// Array.map var arr = [...new Array(10).keys()]; console.log("array", arr) arr = arr.map((value, index) => ++value) console.log("mapped array", arr) // new property Object.defineProperty(Object.prototype, ''mapObject'', { value: function(f, ctx) { ctx = ctx || this; var self = this, result = {}; Object.keys(self).forEach(function(k) { result[k] = f.call(ctx, self[k], k, self); }); return result; } }); // Object.mapObject var obj = new Object() arr = [...new Array(10).keys()]; arr.forEach((value, index) => obj[value] = value) console.log("object", obj) obj = obj.mapObject((value, key) => ++value) console.log("mapped object", obj) console.log("object keys", [...Object.keys(obj)]) console.log("object values", [...Object.keys(obj).map(k => obj[k])])


Adjuntar propiedades a funciones es una forma hermosa (posiblemente inactiva / piratea) de sobrecargar al operador () , que a su vez se usa generalmente para implementar functors : tipos de objetos que tienen un trabajo realmente importante y todas sus otras funcionalidades (si hay es alguno) es solo un grupo de ayudantes. También podría interpretar estos funtores como, básicamente, una función "con estado" donde el estado es público (la mayoría de las funciones en línea, por ejemplo, tienen un estado privado, es decir, un estado del ámbito local).

Este JSFiddle demuestra cómo podemos usar una función con propiedades personalizadas para una función de translator con utilidades adicionales:

/** * Creates a new translator function with some utility methods attached to it. */ var createTranslator = function(dict) { var translator = function(word) { return dict[word]; }; translator.isWordDefined = function(word) { return dict.hasOwnProperty(word); }; // Add more utilities to translator here... return translator; }; // create dictionary var en2deDictionary = { ''banana'': ''Banane'', ''apple'': ''Apfel'' }; // simple use case: var translator = createTranslator(en2deDictionary); var pre = $(''<pre>''); $("body").append(pre); pre.append(translator(''banana'') + ''/n''); pre.append(translator(''apple'') + ''/n''); pre.append(translator.isWordDefined(''w00t'') + ''/n'');

Como puede ver, esto es perfecto para un traductor cuyo único propósito es traducir. Por supuesto, hay muchos más ejemplos de estos tipos de objetos, pero por lejos no son tan comunes como los tipos con funcionalidades diversificadas, como los tipos clásicos de User , Car Animal , etc. Para ese tipo de tipos, solo desea agregar propiedades personalizadas en muy pocos casos. Por lo general, desea definirlos como clases más completas, y tener sus propiedades públicas accesibles a través de this y su prototype .


En primer lugar, es importante darse cuenta de que las propiedades de función estándar (argumentos, nombre, llamante y duración) no se pueden sobrescribir. Por lo tanto, olvídate de agregar una propiedad con ese nombre.

Agregar sus propias propiedades personalizadas a una función se puede hacer de diferentes maneras que deberían funcionar en cada navegador.

Agregar sus propias propiedades personalizadas a una función

Modo 1: agregar propiedades al ejecutar la función:

var doSomething = function() { doSomething.name = ''Tom''; doSomething.name2 = ''John''; return ''Beep''; }; console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2); console.log(''doSomething() : '' + doSomething()); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2);

Salida:

doSomething.name : doSomething.name2 : undefined doSomething() : Beep doSomething.name : doSomething.name2 : John

Camino 1 (sintaxis alternativa):

function doSomething() { doSomething.name = ''Tom''; doSomething.name2 = ''John''; return ''Beep''; }; console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2); console.log(''doSomething() : '' + doSomething()); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2);

Salida:

doSomething.name : doSomething doSomething.name2 : undefined doSomething() : Beep doSomething.name : doSomething doSomething.name2 : John

Camino 1 (segunda sintaxis alternativa):

var doSomething = function f() { f.name = ''Tom''; f.name2 = ''John''; return ''Beep''; }; console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2); console.log(''doSomething() : '' + doSomething()); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2);

Salida:

doSomething.name : f doSomething.name2 : undefined doSomething() : Beep doSomething.name : f doSomething.name2 : John

Un problema con esta estrategia es que necesita ejecutar su función al menos una vez para asignar las propiedades. Para muchas funciones, obviamente eso no es lo que quieres. Entonces, consideremos las otras opciones.

Modo 2: agregar propiedades después de definir la función:

function doSomething() { return ''Beep''; }; doSomething.name = ''Tom''; doSomething.name2 = ''John''; console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2); console.log(''doSomething() : '' + doSomething()); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2);

Salida:

doSomething.name : doSomething doSomething.name2 : John doSomething() : Beep doSomething.name : doSomething doSomething.name2 : John

Ahora, no necesita ejecutar su función primero antes de que pueda acceder a sus propiedades. Sin embargo, una desventaja es que sus propiedades se sienten desconectadas de su función.

Forma 3: ajuste su función en función anónima:

var doSomething = (function(args) { var f = function() { return ''Beep''; }; for (i in args) { f[i] = args[i]; } return f; }({ ''name'': ''Tom'', ''name2'': ''John'' })); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2); console.log(''doSomething() : '' + doSomething()); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2);

Salida:

doSomething.name : doSomething.name2 : John doSomething() : Beep doSomething.name : doSomething.name2 : John

Envolviendo su función en una función anónima, puede recopilar sus atributos en un objeto y usar un bucle para agregar esos atributos uno por uno dentro de la función anónima. De esta forma, sus atributos se sienten más conectados a su función. Esta técnica también es muy útil para cuando tus atributos necesitan ser copiados de un objeto existente. Una desventaja, sin embargo, es que solo puede agregar múltiples atributos al mismo tiempo cuando define su función. Además, no resulta exactamente en el código DRY si agregar propiedades a una función es algo que desea hacer a menudo.

Forma 4: agregue una función ''extender'' a su función, que agrega las propiedades de un objeto a sí mismo una por una:

var doSomething = function() { return ''Beep''; }; doSomething.extend = function(args) { for (i in args) { this[i] = args[i]; } return this; } doSomething.extend({ ''name'': ''Tom'', ''name2'': ''John'' }); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2); console.log(''doSomething() : '' + doSomething()); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2);

Salida:

doSomething.name : doSomething.name2 : John doSomething() : Beep doSomething.name : doSomething.name2 : John

De esta forma, puede extender múltiples propiedades y / o copiar propiedades desde otro proyecto en cualquier momento. De nuevo, sin embargo, su código no es SECO si esto es algo que hace más a menudo.

Forma 5: realice una función genérica de ''extensión'':

var extend = function(obj, args) { if (isArray(args) || (args !== null && typeof args === ''object'')) { for (i in args) { this[i] = args[i]; } } return obj; } var Job = extend( function() { return ''Beep''; }, { ''name'': ''Tom'', ''name2'': ''John'' } ); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2); console.log(''doSomething() : '' + doSomething()); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2);

Salida:

doSomething.name : doSomething.name2 : John doSomething() : Beep doSomething.name : doSomething.name2 : John

Una función de extensión genética permite un enfoque más DRY, lo que le permite agregar el objeto o cualquier proyecto a cualquier otro objeto.

Modo 6: crea un objeto ExtendableFunction y úsalo para adjuntar una función de extensión a una función:

var extendableFunction = (function() { var extend = function(args) { if (isArray(args) || (args !== null && typeof args === ''object'')) { for (i in args) { this[i] = args[i]; } } return this; }; var ef = function(v, obj) { v.extend = extend; return v.extend(obj); }; ef.create = function(v, args) { return new this(v, args); }; return ef; })(); var doSomething = extendableFunction.create( function() { return ''Beep''; }, { ''name'': ''Tom'', ''name2'': ''John'' } ); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2); console.log(''doSomething() : '' + doSomething()); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2);

Salida:

doSomething.name : doSomething.name2 : John doSomething() : Beep doSomething.name : doSomething.name2 : John

En lugar de utilizar una función genérica de "extensión", esta técnica le permite generar funciones que tienen un método de "extensión" adjunto.

Modo 7: agregue una función ''extender'' al prototipo de función:

Function.prototype.extend = function(args) { if (isArray(args) || (args !== null && typeof args === ''object'')) { for (i in args) { this[i] = args[i]; } } return this; }; var doSomething = function() { return ''Beep''; }.extend({ name : ''Tom'', name2 : ''John'' }); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2); console.log(''doSomething() : '' + doSomething()); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2);

Salida:

doSomething.name : doSomething.name2 : John doSomething() : Beep doSomething.name : doSomething.name2 : John

Una gran ventaja de esta técnica es que hace que agregar nuevas propiedades a una función sea muy fácil y SECO así como también completamente OO. Además, es bastante amigable con la memoria. Un inconveniente, sin embargo, es que no es muy a prueba de futuro. En caso de que los navegadores futuros agreguen una función nativa ''extender'' al prototipo de Función, esto podría romper su código.

Forma 8: ejecute una función de forma recursiva una vez y luego regrésela:

var doSomething = (function f(arg1) { if(f.name2 === undefined) { f.name = ''Tom''; f.name2 = ''John''; f.extend = function(obj, args) { if (isArray(args) || (args !== null && typeof args === ''object'')) { for (i in args) { this[i] = args[i]; } } return obj; }; return f; } else { return ''Beep''; } })(); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2); console.log(''doSomething() : '' + doSomething()); console.log(''doSomething.name : '' + doSomething.name); console.log(''doSomething.name2 : '' + doSomething.name2);

Salida:

doSomething.name : f doSomething.name2 : John doSomething() : Beep doSomething.name : f doSomething.name2 : John

Ejecute una función una vez y haga que pruebe si una de sus propiedades está configurada. Si no está configurado, establezca las propiedades y regrese a sí mismo. Si está configurado, ejecuta la función. Si incluye una función ''extender'' como una de las propiedades, puede ejecutarla para agregar nuevas propiedades.

Agregar sus propias propiedades personalizadas a un objeto

A pesar de todas estas opciones, no obstante, recomendaría no agregar propiedades a una función. ¡Es mucho mejor agregar propiedades a los objetos!

Personalmente, prefiero las clases singleton con la siguiente sintaxis.

var keyValueStore = (function() { return { ''data'' : {}, ''get'' : function(key) { return keyValueStore.data[key]; }, ''set'' : function(key, value) { keyValueStore.data[key] = value; }, ''delete'' : function(key) { delete keyValueStore.data[key]; }, ''getLength'' : function() { var l = 0; for (p in keyValueStore.data) l++; return l; } } })();

Una ventaja de esta sintaxis es que permite variables tanto públicas como privadas. Por ejemplo, así es como haces que la variable ''datos'' sea privada:

var keyValueStore = (function() { var data = {}; return { ''get'' : function(key) { return data[key]; }, ''set'' : function(key, value) { data[key] = value; }, ''delete'' : function(key) { delete data[key]; }, ''getLength'' : function() { var l = 0; for (p in data) l++; return l; } } })();

¿Pero quieres múltiples instancias de almacén de datos, dices? ¡No hay problema!

var keyValueStore = (function() { var count = -1; return (function kvs() { count++; return { ''data'' : {}, ''create'' : function() { return new kvs(); }, ''count'' : function() { return count; }, ''get'' : function(key) { return this.data[key]; }, ''set'' : function(key, value) { this.data[key] = value; }, ''delete'' : function(key) { delete this.data[key]; }, ''getLength'' : function() { var l = 0; for (p in this.data) l++; return l; } } })(); })();

Finalmente, puede separar las propiedades de instancia e individual y usar un prototipo para los métodos públicos de la instancia. Eso resulta en la siguiente sintaxis:

var keyValueStore = (function() { var count = 0; // Singleton private properties var kvs = function() { count++; // Instance private properties this.data = {}; // Instance public properties }; kvs.prototype = { // Instance public properties ''get'' : function(key) { return this.data[key]; }, ''set'' : function(key, value) { this.data[key] = value; }, ''delete'' : function(key) { delete this.data[key]; }, ''getLength'' : function() { var l = 0; for (p in this.data) l++; return l; } }; return { // Singleton public properties ''create'' : function() { return new kvs(); }, ''count'' : function() { return count; } }; })();

Con esta sintaxis, puedes tener:

  • múltiples instancias de un objeto
  • variables privadas
  • variables de clase

Lo usas así:

kvs = keyValueStore.create(); kvs.set(''Tom'', "Baker"); kvs.set(''Daisy'', "Hostess"); var profession_of_daisy = kvs.get(''Daisy''); kvs.delete(''Daisy''); console.log(keyValueStore.count());


Es perfectamente aceptable agregar propiedades o métodos a un objeto de función. Se hace con bastante frecuencia. El objeto jQuery / $ es un ejemplo de esto. Es una función con bastantes métodos adjuntos.

Cuando se agregan propiedades a un constructor, se llaman propiedades "estáticas" y se pueden invocar sin una instancia de la clase. por ejemplo, Object.create.

No tengo suficientes representantes para escribir un comentario, por lo que diré aquí: en general, se consideró una mala práctica ampliar los prototipos de objetos incorporados, especialmente si el código tiene que jugar con el código de otras personas. Puede tener consecuencias impredecibles difíciles de rastrear.


Es un poco difícil dar una respuesta muy significativa a tu pregunta, porque de alguna manera has dicho "Aquí está mi solución, ¿está bien?" sin explicar qué problema estás tratando de resolver (incluso dijiste explícitamente que no vas a explicar el "por qué"). Su código parece ser JavaScript válido que se ejecutará, pero también parece una manera menos que óptima de hacer las cosas.

Si explica lo que realmente quiere lograr, puede obtener algunas buenas sugerencias sobre mejores formas de estructurar su código. Aún así, te daré algún tipo de respuesta:

¿Se puede considerar este método como "adecuado" y compatible con los estándares? Funciona en Firefox, pero hay muchas cosas que funcionan como se espera en los navegadores web y de ninguna manera son estándares.

Las funciones son objetos (como usted dijo) y, por lo tanto, es posible agregarles propiedades. Esto no es realmente un problema de estándares, ya que es una parte central de JavaScript compatible con todos los navegadores.

¿Es este tipo de alteración de objetos al agregarles nuevas propiedades una buena práctica?

Es su objetivo, puede agregar las propiedades que desee. El objetivo de los objetos es que tienen propiedades que puedes manipular. Realmente no puedo imaginar una forma de usar objetos que no implique alterarlos, incluida la adición, eliminación y actualización de propiedades y métodos.

Habiendo dicho eso, para mí no tiene sentido agregar propiedades a la función myMethod , sería más habitual agregar otras propiedades a su objeto something (su función myMethod , si se llama correctamente, tendría acceso a las otras propiedades). de something través de this palabra clave).

Si está utilizando una función como constructor , normalmente tiene sentido agregar métodos al prototipo asociado y agregar propiedades (sin método) a cada instancia, pero puede hacer una o ambas cosas de la otra manera cuando sea apropiado. (Observando que un "método" es esencialmente solo una propiedad que sucede al hacer referencia a una función).

El código específico que ha mostrado no agrega propiedades, prueba si la propiedad someProperty ya existe y, si es así, le asigna un nuevo valor.

Puede beneficiarse leyendo algunos artículos como estos en MDN:



Si solo desea agregar propiedades personalizadas a una función, entonces solo necesita agregar esas propiedades a Function.prototype. Por ejemplo:

Function.prototype.SomeNewProperty = function () {//Do something awesome here...}