in

¿Cómo puedo garantizar que mi definición de enumeraciones no cambie en JavaScript?

apple touch icon@2

Vayamos directamente al problema: el tamaño del archivo. Todas las demás respuestas enumeradas aquí inflan su código minimizado al extremo. Les presento que para la mejor reducción posible en el tamaño del código mediante la minificación, el rendimiento, la legibilidad del código, la gestión de proyectos a gran escala y las sugerencias de sintaxis en muchos editores de código, esta es la forma correcta de hacer enumeraciones: variables de notación de subrayado.


Como se demuestra en el cuadro anterior y el ejemplo a continuación, aquí hay cinco sencillos pasos para comenzar:

  1. Determine un nombre para el grupo de enumeración. Piense en un sustantivo que pueda describir el propósito de la enumeración o al menos las entradas en la enumeración. Por ejemplo, un grupo de enumeraciones que representan colores que puede elegir el usuario podría llamarse mejor COLORCHOICES que COLORES.
  2. Decidir si las enumeraciones en el grupo son mutuamente excluyentes o independientes. Si son mutuamente excluyentes, comience cada nombre de variable enumerado con ENUM_. Si es independiente o en paralelo, utilice INDEX_.
  3. Para cada entrada, cree una nueva variable local cuyo nombre comience con ENUM_ o INDEX_, luego el nombre del grupo, luego un guión bajo, luego un nombre descriptivo único para la propiedad
  4. Agrega un ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_, o INDEXLEN_ (ya sea LEN_ o LENGTH_ es preferencia personal) variable enumerada al final. Debe usar esta variable siempre que sea posible en su código para asegurarse de que agregar una entrada adicional a la enumeración e incrementar este valor no romperá su código.
  5. Asigne a cada variable enumerada sucesivamente un valor uno más que la anterior, comenzando en 0. Hay comentarios en esta página que dicen 0 no debe usarse como un valor enumerado porque 0 == null, 0 == false, 0 == "", y otras locuras de JS. Les presento que, para evitar este problema y aumentar el rendimiento al mismo tiempo, utilice siempre === y nunca dejes == aparecen en su código excepto con typeof (ex typeof X == "string"). En todos mis años de uso ===, Nunca he tenido un problema con el uso de 0 como valor de enumeración. Si todavía eres aprensivo, entonces 1 podría usarse como valor inicial en ENUM_ enumeraciones (pero no en INDEX_ enumeraciones) sin penalización de desempeño en muchos casos.
const ENUM_COLORENUM_RED   = 0;
const ENUM_COLORENUM_GREEN = 1;
const ENUM_COLORENUM_BLUE  = 2;
const ENUMLEN_COLORENUM    = 3;

// later on

if(currentColor === ENUM_COLORENUM_RED) {
   // whatever
}

Así es como recuerdo cuándo usar INDEX_ y cuando usar ENUM_:

// Precondition: var arr = []; //
arr[INDEX_] = ENUM_;

Sin embargo, ENUM_ puede, en determinadas circunstancias, ser apropiado como índice, como cuando se cuentan las ocurrencias de cada elemento.

const ENUM_PET_CAT = 0,
      ENUM_PET_DOG = 1,
      ENUM_PET_RAT = 2,
      ENUMLEN_PET  = 3;

var favoritePets = [ENUM_PET_CAT, ENUM_PET_DOG, ENUM_PET_RAT,
                    ENUM_PET_DOG, ENUM_PET_DOG, ENUM_PET_CAT,
                    ENUM_PET_RAT, ENUM_PET_CAT, ENUM_PET_DOG];

var petsFrequency = [];

for (var i=0; i<ENUMLEN_PET; i=i+1|0)
  petsFrequency[i] = 0;

for (var i=0, len=favoritePets.length|0, petId=0; i<len; i=i+1|0)
  petsFrequency[petId = favoritePets[i]|0] = (petsFrequency[petId]|0) + 1|0;

console.log({
    "cat": petsFrequency[ENUM_PET_CAT],
    "dog": petsFrequency[ENUM_PET_DOG],
    "rat": petsFrequency[ENUM_PET_RAT]
});

Observe que, en el código anterior, es muy fácil agregar un nuevo tipo de mascota: solo tendría que agregar una nueva entrada después de ENUM_PET_RAT y actualizar ENUMLEN_PET respectivamente. Podría ser más difícil y con errores agregar una nueva entrada en otros sistemas de enumeración.


Además, esta sintaxis de enumeraciones permite una extensión de clase clara y concisa como se ve a continuación. Para extender una clase, agregue un número creciente al LEN_ entrada de la clase padre. Luego, termine la subclase con su propia LEN_ entrada para que la subclase pueda extenderse más en el futuro.

Diagrama de extensión de adición

(function(window){
    "use strict";
    var parseInt = window.parseInt;

    // use INDEX_ when representing the index in an array instance
    const INDEX_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE
          INDEXLEN_PIXELCOLOR   = 1,
          INDEX_SOLIDCOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_SOLIDCOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_SOLIDCOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEXLEN_SOLIDCOLOR   = INDEXLEN_PIXELCOLOR+3,
          INDEX_ALPHACOLOR_R    = INDEXLEN_PIXELCOLOR+0,
          INDEX_ALPHACOLOR_G    = INDEXLEN_PIXELCOLOR+1,
          INDEX_ALPHACOLOR_B    = INDEXLEN_PIXELCOLOR+2,
          INDEX_ALPHACOLOR_A    = INDEXLEN_PIXELCOLOR+3,
          INDEXLEN_ALPHACOLOR   = INDEXLEN_PIXELCOLOR+4,
    // use ENUM_ when representing a mutually-exclusive species or type
          ENUM_PIXELTYPE_SOLID = 0,
          ENUM_PIXELTYPE_ALPHA = 1,
          ENUM_PIXELTYPE_UNKNOWN = 2,
          ENUMLEN_PIXELTYPE    = 2;

    function parseHexColor(inputString) {
        var rawstr = inputString.trim().substring(1);
        var result = [];
        if (rawstr.length === 8) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16);
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 4) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA;
            result[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
            result[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11;
        } else if (rawstr.length === 6) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16);
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16);
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16);
        } else if (rawstr.length === 3) {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID;
            result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11;
            result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11;
        } else {
            result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN;
        }
        return result;
    }

    // the red component of green
    console.log(parseHexColor("#0f0")[INDEX_SOLIDCOLOR_R]);
    // the alpha of transparent purple
    console.log(parseHexColor("#f0f7")[INDEX_ALPHACOLOR_A]); 
    // the enumerated array for turquoise
    console.log(parseHexColor("#40E0D0"));
})(self);

(Longitud: 2.450 bytes)

Algunos pueden decir que esto es menos práctico que otras soluciones: desperdicia toneladas de espacio, lleva mucho tiempo escribir y no está cubierto de sintaxis de azúcar. Esas personas tendrían razón si no minimizan su código. Sin embargo, ninguna persona razonable dejaría un código sin modificar en el producto final. Para esta minificación, Closure Compiler es lo mejor que tengo que encontrar. Se puede encontrar acceso en línea aquí. El compilador de cierre puede tomar todos estos datos de enumeración e incorporarlos, lo que hace que su Javascript sea súper pequeño y se ejecute súper rápido. Por lo tanto, Minify con Closure Compiler. Observar.


El compilador de cierres puede realizar algunas optimizaciones bastante increíbles a través de inferencias que están mucho más allá de las capacidades de cualquier otro minificador de Javascript. Closure Compiler es capaz de insertar variables primitivas en un valor fijo. Closure Compiler también puede hacer inferencias basadas en estos valores en línea y eliminar bloques no utilizados en sentencias if y bucles.

Extracción de código a través del compilador de cierre

'use strict';(function(e){function d(a){a=a.trim().substring(1);var b=[];8===a.length?(b[0]=1,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16),b[4]=c(a.substring(4,6),16)):4===a.length?(b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16),b[4]=17*c(a[3],16)):6===a.length?(b[0]=0,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16)):3===a.length?(b[0]=0,b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16)):b[0]=2;return b}var c=
e.parseInt;console.log(d("#0f0")[1]);console.log(d("#f0f7")[4]);console.log(d("#40E0D0"))})(self);

(Longitud: 605 bytes)

Closure Compiler lo recompensa por codificar de manera más inteligente y organizar bien su código porque, mientras que muchos minificadores castigan el código organizado con un tamaño de archivo minificado más grande, Closure Compiler puede examinar toda su limpieza y cordura para generar un tamaño de archivo aún más pequeño si usa trucos como enumeraciones de nombres de variables. Eso, en esta única mente, es el santo grial de la codificación: una herramienta que ayuda a su código con un tamaño minificado más pequeño y ayuda a su mente al entrenar mejores hábitos de programación.


Ahora, veamos qué tan grande sería el archivo equivalente sin ninguna de estas enumeraciones.

Fuente sin usar enumeraciones (longitud: 1.973 bytes (477 bytes más corto que el código enumerado))
Minificado sin usar enumeraciones (longitud: 843 bytes (238 bytes más largo que el código enumerado))

Tabla de tamaños de código

Como se ve, sin enumeraciones, el código fuente es más corto a costa de un código minificado más grande. No sé sobre ti; pero estoy seguro de que no incorporo el código fuente en el producto final. Por lo tanto, esta forma de enumeraciones es muy superior en la medida en que da como resultado tamaños de archivo minificados más pequeños.


Otra ventaja de esta forma de enumeración es que se puede utilizar para gestionar fácilmente proyectos a gran escala sin sacrificar el tamaño del código minificado. Cuando se trabaja en un proyecto grande con muchas otras personas, puede ser beneficioso marcar y etiquetar explícitamente los nombres de las variables con quién creó el código para que el creador original del código pueda identificarse rápidamente para la corrección de errores en colaboración.

// JG = Jack Giffin
const ENUM_JG_COLORENUM_RED   = 0,
      ENUM_JG_COLORENUM_GREEN = 1,
      ENUM_JG_COLORENUM_BLUE  = 2,
      ENUMLEN_JG_COLORENUM    = 3;

// later on

if(currentColor === ENUM_JG_COLORENUM_RED) {
   // whatever
}

// PL = Pepper Loftus
// BK = Bob Knight
const ENUM_PL_ARRAYTYPE_UNSORTED   = 0,
      ENUM_PL_ARRAYTYPE_ISSORTED   = 1,
      ENUM_BK_ARRAYTYPE_CHUNKED    = 2, // added by Bob Knight
      ENUM_JG_ARRAYTYPE_INCOMPLETE = 3, // added by jack giffin
      ENUMLEN_PL_COLORENUM         = 4;

// later on

if(
  randomArray === ENUM_PL_ARRAYTYPE_UNSORTED ||
  randomArray === ENUM_BK_ARRAYTYPE_CHUNKED
) {
   // whatever
}

Además, esta forma de enumeración también es mucho más rápida después de la minificación. En las propiedades con nombre normales, el navegador tiene que usar hashmaps para buscar dónde está la propiedad en el objeto. Aunque los compiladores JIT almacenan en caché de manera inteligente esta ubicación en el objeto, todavía hay una enorme sobrecarga debido a casos especiales, como eliminar una propiedad inferior del objeto.

lattice.svg

Pero, con índices enteros no dispersos continuos PACKED_ELEMENTS matrices, el navegador puede omitir gran parte de esa sobrecarga porque el índice del valor en la matriz interna ya está especificado. Sí, de acuerdo con el estándar ECMAScript, se supone que todas las propiedades deben tratarse como cadenas. Sin embargo, este aspecto del estándar ECMAScript es muy engañoso sobre el rendimiento porque todos los navegadores tienen optimizaciones especiales para índices numéricos en matrices.

/// Hashmaps are slow, even with JIT juice
var ref = {};
ref.count = 10;
ref.value = "foobar";

Compara el código …

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

JUnit – Descripción general

9S7t6Kmqq3TpBvVBk4zomY 1200 80

«¡Silenzio Bruno!» El director de Luca explica el significado del maravilloso mantra de la película de Pixar