2010-07-08 12 views
26

Actualmente estoy manteniendo una gran cantidad de archivos JS y el problema de la dependencia está creciendo sobre mi cabeza. En este momento tengo cada función en un archivo separado y mantengo manualmente una base de datos para resolver las dependencias entre funciones.Administración de dependencias de JavaScript

Esto me gustaría automatizar. Por ejemplo si tengo la función f

Array.prototype.f = function() {}; 

que se hace referencia en otra función g

MyObject.g = function() { 
    var a = new Array(); 
    a.f(); 
}; 

Quiero ser capaz de detectar que g es referencia f.

¿Cómo puedo hacer esto? ¿Dónde empiezo? ¿Debo escribir realmente un compilador o puedo modificar a Spidermonkey, por ejemplo? ¿Alguien más ya hizo esto?

Cualquier indicador de ayuda para empezar se aprecia mucho

Gracias Dok

+0

¿Desea generar un gráfico que muestre qué funciones son llamadas por qué otras funciones? Eso es lo que estoy tratando de hacer actualmente, y me pregunto si estás tratando de resolver el mismo problema que yo. :) –

+1

@AndersonGreen ¿Estás buscando algo como [Code2Flow] (https://github.com/scottrogowski/code2flow)? –

Respuesta

18

Mientras que teóricamente podría escribir una herramienta de análisis estático que detecta el uso de variables globales definidas en otros archivos, como el uso de MyObject, no podría realizar un seguimiento realista del uso de los métodos de extensión prototype.

JavaScript es un lenguaje de tipo dinámico así que no hay forma práctica de cualquier herramienta para saber que a, si pasado de la función g, es un Array, y por lo que si f() se llama en ella hay una dependencia. Solo se determina qué variables contienen qué tipos en tiempo de ejecución, por lo que debe saber que necesita un intérprete y que se ha convertido en un problema completo de Turing.

por no mencionar el otros aspectos dinámicos de JavaScript que desafían completamente análisis estático, como ir a buscar propiedades por notación de corchetes, la temida eval, o cadenas en tiempos de espera o atributos de evento de controlador.

Creo que es un poco difícil de arrancar realmente. Probablemente sea mejor que rastree las dependencias manualmente, pero simplifíquelas agrupando las funciones relacionadas en módulos que serán su unidad básica de seguimiento de dependencias. OK, obtendrá algunas funciones más que técnicamente necesita, pero espero que no demasiado.

También es una buena idea ponerle nombre a cada módulo, por lo que está muy claro hacia dónde va cada llamada, lo que facilita el control manual de las dependencias (por ejemplo, un comentario // uses: ThisModule, ThatModule en la parte superior).

Dado que las extensiones de los prototipos incorporados son más difíciles de seguir, manténgalas al mínimo. Extendiendo, por ej. Array para incluir los métodos de Quinta Edición de ECMAScript (como indexOf) en navegadores que aún no los tienen es una buena cosa que hacer como corrección básica que usarán todos los scripts. Agregar funcionalidad arbitraria completamente nueva a los prototipos existentes es cuestionable.

+0

Gracias por su respuesta ... Tomaré una ruta diferente entonces. – user386508

8

Como @bobince ya sugirió, hacer un análisis estático en un programa de JavaScript es un problema casi imposible de descifrar. Google Closure compiler lo hace hasta cierto punto, pero luego también se basa en la ayuda externa de JSDoc comentarios.

Tenía un similar problem de encontrar el orden en que los archivos JS deberían concatenarse en un proyecto anterior, y dado que había muchos archivos JS, la actualización manual del orden de inclusión parecía demasiado tediosa. En cambio, me quedé con ciertas convenciones de lo que constituye una dependencia para mis propósitos, y en base a eso y usando el simple regexp :) Pude generar el orden de inclusión correcto.

La solución usó un algoritmo topological sort para generar un dependency graph que luego enumeró los archivos en el orden en que deberían incluirse para satisfacer todas las dependencias. Como cada archivo era básicamente una pseudoclase que usaba la sintaxis MooTools, solo había 3 formas en las que se podían crear dependencias para mi situación.

  1. Cuando una clase Extendió alguna otra clase.
  2. Cuando una clase implementó alguna otra clase.
  3. Cuando una clase creó una instancia de un objeto de alguna otra clase con la palabra clave new.

Era una solución simple, y definitivamente una solución rota para uso general, pero me fue muy útil. Si está interesado en la solución, puede ver el code here, está en Ruby.

Si sus dependencias son más complejos, entonces tal vez usted podría hacer una lista manualmente las dependencias en cada JS presentar a sí misma utilizando comentarios y una sintaxis de cosecha propia, tales como:

// requires: Array 
// requires: view/TabPanel 
// requires: view/TabBar 

Luego lee cada archivo JS, omita la requiere comentarios, y construya un gráfico de dependencia que le dará la orden de inclusión que necesita.

5

Sería bueno tener una herramienta que pueda detectar automáticamente esas dependencias y elegir cómo se cargan. Las mejores soluciones hoy en día son un poco más crudas. Creé un administrador de dependencias para mis necesidades particulares que deseo agregar a la lista (Pyramid Dependency Manager). Tiene algunas características clave que resuelven algunos casos de uso únicos.

  1. manijas de otros archivos (incluyendo la inserción de HTML para las vistas ... sí, se puede separar sus puntos de vista durante el desarrollo)
  2. combina los archivos para usted en javascript cuando esté listo para el lanzamiento (sin necesidad de instalar externa herramientas)
  3. Tiene una inclusión genérica para todas las páginas html. Solo tiene que actualizar un archivo cuando se agrega, elimina, renombra, etc.

Algunos ejemplos de código para mostrar cómo funciona durante el desarrollo.

del archivo: dependencyLoader.js

//Set up file dependencies 
Pyramid.newDependency({ 
    name: 'standard', 
    files: [ 
    'standardResources/jquery.1.6.1.min.js' 
    ] 
}); 

Pyramid.newDependency({ 
name:'lookAndFeel', 
files: [ 
    'styles.css', 
    'customStyles.css', 
    'applyStyles.js' 
    ] 
}); 

Pyramid.newDependency({ 
name:'main', 
files: [ 
    'createNamespace.js', 
    'views/buttonView.view', //contains just html code for a jquery.tmpl template 
    'models/person.js', 
    'init.js' 
    ], 
    dependencies: ['standard','lookAndFeel'] 
}); 

los archivos HTML

<head> 
    <script src="standardResources/pyramid-1.0.1.js"></script> 
    <script src="dependencyLoader.js"></script> 
    <script type="text/javascript"> 
     Pyramid.load('main'); 
    </script> 
</head> 

Se le requiere para mantener un único archivo para administrar dependencias.Estoy pensando en crear un programa que pueda generar automáticamente el archivo de carga basado en includes en el encabezado, pero dado que maneja muchos tipos diferentes de dependencias, mantenerlas en un archivo podría ser mejor.

1

JSAnalyse utiliza el análisis de código estático para detectar dependencias entre los archivos javascript: http://jsanalyse.codeplex.com/

También le permite definir las dependencias permitidas y para asegurarse de que durante la construcción, por ejemplo. Por supuesto, no puede detectar todas las dependencias porque javascript es un lenguaje de interpretación dinámico que no es seguro para tipos, como ya se mencionó. Pero al menos te hace consciente de tu gráfico de dependencia de JavaScript y te ayuda a mantenerlo bajo control.

10

¿Ha intentado utilizar un administrador de dependencia como RequireJS o LabJS? Noté que nadie los mencionó en este hilo.

De http://requirejs.org/docs/start.html:

Dentro de main.js, puede utilizar require() para cargar todos los otros scripts que necesidad de ejecutar:

require(["helper/util"], function(util) { 
    //This function is called when scripts/helper/util.js is loaded. 
    //If util.js calls define(), then this function is not fired until 
    //util's dependencies have loaded, and the util argument will hold 
    //the module value for "helper/util". 
}); 

puede anidar esas dependencias también, entonces helper/util puede requerir algunos otros archivos dentro de sí mismo.

0

He escrito una herramienta para hacer algo como esto: http://github.com/damonsmith/js-class-loader

Es más útil si tiene una aplicación web Java y la estructura de su código JS en el estilo de Java. Si lo hace, puede detectar todas sus dependencias de código y agruparlas, con soporte para dependencias tanto de tiempo de ejecución como de tiempo de análisis.

Cuestiones relacionadas