Opción ADVANCED_OPTIMIZATIONS de Google Closure Compiler

He estado revisando el compilador de cierre de Google recientemente. Descargué el archivo .jar y lo probé. Hasta ahora, debo decir que me ha impresionado mucho. Ciertamente puedo ver su utilidad más allá de la minimización. ¡Apoyos al equipo de Google!

Sin embargo, tengo una pequeña queja. Me parece que solo tienes dos opciones en lo que respecta a la optimización. Es SIMPLE_OPTIMIZACIONES u ADVANCED_OPTIMIZACIONES. El primero, aunque adecuado, es muy simple en mi humilde opinión. Por un lado, a menos que me esté perdiendo algo, deja intactos todos los nombres de propiedad. Tampoco elimina el código inalcanzable. Por otro lado, la última opción es simplemente demasiado destructiva.

Ahora, soy bastante nuevo en JavaScript, por lo que es muy probable que me esté perdiendo algo. Si digo algo estúpido, siéntete libre de educarme. Dicho esto, puedo entender los problemas con el cambio de nombre en JavaScript. El equipo de Google recomienda usar la notación de corchetes (objeto['propiedad']) en lugar de la notación de puntos (objeto.propiedad) para acceder a las propiedades que no desea cambiar y nunca mezclar los dos usos. También sugieren métodos de "exportación" utilizando el siguiente patrón:

MyClass = function(name) {
  this.myName = name;
};

MyClass.prototype.myMethod = function() {
  alert(this.myName);
};

window['MyClass'] = MyClass; // <-- Constructor
MyClass.prototype['myMethod'] = MyClass.prototype.myMethod;

Sin embargo, hay casos legítimos en los que desea mezclar las dos notaciones. Digamos que estamos construyendo un juego. El código del juego está completamente aislado dentro de un cierre. No 'exporta' nada al ámbito global, ni necesita hacerlo. De hecho, realmente no debería tocar el objeto de la ventana. De todos modos, eso necesita leer algunas propiedades del juego de los archivos de configuración XML.

Ejemplo de JavaScript:

var TheGreatAdventure = (function(window) {

    function Fighter() {
        // Private to application
        this.id        = 42;
        // Accessible to XML configuration system
        this.name      = 'Generic Jen';
        this.hitPoints = 100;
        this.onAttack  = genericFighterAttack;
        this.onSpeak   = genericFighterSpeak;
        ...
    }
    Fighter.publishedProperties = ['name', 'hitPoints', 'onAttack', 'onSpeak']

    function genericFighterAttack() {...}
    function genericFighterSpeak() {...}

    function cassieAttack() {...}
    function cassieSpeak() {...}

    ...

    EntityReader = {
        ...
        function readFromXMLNode(attributes, entityClass, entityInstance) {
            for (var i = 0; i < attributes.length; i++) {
                var attribute = attributes[i];
                if (attribute.nodeName in entityClass.publishedProperties)
                    entityInstance[attribute.nodeName] = bindContext[attribute.value];
            }
        }
        ...
    }

}(window));

Ejemplo de archivo de configuración XML:

<Fighter name='Custom Cassie' onAttack='cassieAttack' onSpeak='cassieSpeak'/>

¡El sistema anterior no solo fallaría en asignar las propiedades, sino que las funciones cassieAttack y cassieSpeak se habrían eliminado durante la minimización como código muerto!

Ahora, no hay forma de que acceda a todas las propiedades 'publicadas' usando la notación de paréntesis en todo el código del juego. Incluso si no hay una penalización de tiempo de ejecución al hacerlo (no debería haber ninguna), todavía hay mucho tipeo adicional involucrado y es (en mi opinión) una monstruosidad. Con propiedades tan comunes, todo se mostraría como una cadena dentro de un editor de texto, ¡lo que anularía el propósito del resaltado de sintaxis!

Me parece que una simple directiva @preserve (o algo similar) sobre esas propiedades permitiría usar ADVANCED_OPTIMIZATIONS con un costo mínimo en el tamaño final del programa. ¿Me estoy perdiendo de algo?

preguntado el 03 de mayo de 12 a las 15:05

Tengo una respuesta a una pregunta similar: stackoverflow.com/questions/7823811/… -

2 Respuestas

Esta respuesta se reescribió por completo, resulta que hay una manera de hacer lo que quiere el usuario1127813.

Debe proporcionar un archivo de mapeo de propiedades que mapee algunos nombres a sí mismos, usando el --property_map_input_file bandera. Suponga que tiene el siguiente código original en test.js:

/** @constructor */
function Fighter() {
    this.ID        = 42;
    this.fullName  = 'Generic Jen';
    this.hitPoints = 100;
}
Fighter.publishedProperties = ['fullName', 'hitPoints'];

var jen = new Fighter();
var bob = new Fighter();

bob.ID = 54;
bob.fullName = 'Bob the Destructor';
bob.hitPoints = 1337;

for(i = 0; i < Fighter.publishedProperties.length; i++) {
    prop = Fighter.publishedProperties[i];
    alert(prop + ' = ' + bob[prop]);
}

Compílalo así:

java -jar closure-compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js test.js --property_map_output_file testprop.txt --js_output_file test2.js

Obtendrás un nuevo archivo. test2.js (con contenidos que no funcionan) y otro archivo testprop.txt eso contiene:

ID:a
hitPoints:c
fullName:b

Cambiar testprop.txt por lo que se ve así:

ID:ID
hitPoints:hitPoints
fullName:fullName

Luego vuelve a compilar con testprop.txt como entrada en lugar de salida:

java -jar closure-compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js test.js --property_map_input_file testprop.txt --js_output_file test2.js

Observar el contenido de test2.js:

var a=["fullName","hitPoints"],b=new function(){};b.ID=54;b.fullName="Bob the Destructor";b.hitPoints=1337;for(i=0;i<a.length;i++)prop=a[i],alert(prop+" = "+b[prop]);

Ahora se accede a las propiedades deseadas con su nombre original usando la notación de puntos y el programa mostrará correctamente ventanas emergentes con las propiedades publicadas de bob.

contestado el 03 de mayo de 12 a las 18:05

Primero que nada, gracias. Sin embargo, ¿no cree que simplemente indicar al módulo de cambio de nombre del compilador que deje en paz una pequeña lista de identificadores sería preferible a tener que usar la notación de corchetes en todo el programa (todas las 40 XNUMX líneas)? Además, ¿qué sucede cuando/si decide usar otro minimizador? Estás atrapado con las cuerdas dentro de los corchetes. Ese es un caso de cambiar algo que no debería (el código del programa) para inducir un efecto secundario (es decir, hacer que el compilador deje ciertas variables en paz). - user1127813

En cuanto al uso del mapa de propiedades, me temo que no es factible, ya que en el ejemplo anterior los archivos XML serán proporcionados por el usuario de la aplicación (creo que no lo aclaré). - user1127813

Tienes razón sobre las funciones. Por supuesto, deberían ser métodos de un objeto (representado por la variable bindContext en readFromXMLNode()). - user1127813

Estoy de acuerdo en que sería preferible tener sintaxis para evitar el cambio de nombre, pero este no es el foro adecuado para cumplir su deseo. Puede enviar sugerencias al equipo de Google Closure en google.com/moderator/?#16/e=49a66 y vote por su sugerencia particular ya enviada en goo.gl/mod/jJPx - jjrv

Si bien el compilador intenta respetar los nombres en un mapa de entrada de propiedades, no es necesario que lo haga. Externos, propiedades citadas y goog.reflect son soluciones más apropiadas. - Chad Killingsworth

El compilador tiene soporte para esto, pero es incómodo y depende de un "primitivo" de Closure Library llamado "goog.reflect.object":

/** @nocollapse */
Fighter.publishedProperties = goog.object.transpose(goog.reflect.object(
    Fighter, {fullName:1, hitPoints:2}));

Esto evita el uso de propiedades cotizadas. Si esto es lo único que usa de Closure Library, todo menos la función "goog.object.transpose" se compilará (goog.object.transpose no es especial, por lo que puede usar una implementación alternativa). Esto es seguro para tipos y se puede usar con las optimizaciones basadas en tipos del compilador (vea una descripción de eso aquí: http://code.google.com/p/closure-compiler/wiki/ExperimentalTypeBasedPropertyRenaming ).

La clave es que goog.reflect.object debe usarse directamente, tomando el constructor y un objeto literal con las claves de las propiedades que necesita conservar.

La otra cosa que querrá tener en cuenta es que en el modo AVANZADO, si Fighter.publishedProperties se define en el ámbito global, se eliminará del constructor debido al colapso del espacio de nombres. @nocollapse previene esto. Una alternativa sería usar un método auxiliar para agregar la propiedad:

function addPublishedProperties(obj, value) {
  obj.publishedProperties = goog.object.transpose(value);
}

Ok, he cubierto mucho terreno aquí, así que asegúrese de hacerme saber si desea una aclaración.

Respondido 17 Abr '15, 17:04

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.