2010-07-08 30 views
26

Attualmente sto mantenendo un numero elevato di file JS e il problema delle dipendenze è in aumento. In questo momento ho ciascuna funzione in un file separato e manterrò manualmente un database per calcolare le dipendenze tra le funzioni.Gestione delle dipendenze JavaScript

Questo mi piacerebbe automatizzare. Per esempio se ho la funzione f

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

che fa riferimento a un'altra funzione g

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

voglio essere in grado di rilevare che g è referenziare f.

Come faccio a fare questo? Da dove comincio? Devo effettivamente scrivere un compilatore o posso modificare Spidermonkey per esempio? Qualcun altro lo ha già fatto?

Tutti gli indicatori per farmi iniziare è molto apprezzato

Grazie Dok

+0

Vuoi generare un grafico che mostri quali funzioni sono chiamate da quali altre funzioni? Questo è quello che sto attualmente cercando di fare, e mi chiedo se stai cercando di risolvere lo stesso problema che io sono. :) –

+1

@AndersonGreen Stai cercando qualcosa come [Code2Flow] (https://github.com/scottrogowski/code2flow)? –

risposta

18

Mentre si potrebbe teoricamente scrivere un tool di analisi statica che ha rilevato l'uso di variabili globali definiti in altri file, come l'uso di MyObject, non è possibile tracciare realisticamente l'utilizzo dei metodi di estensione prototype.

JavaScript è un linguaggio dinamicamente tipizzato in modo non c'è modo pratico per qualsiasi strumento per sapere che a, se approvata dalla funzione g, è un Array, e quindi se f() è chiamato su di esso c'è una dipendenza. Viene determinato solo quali variabili tengono i tipi in fase di esecuzione, in modo da scoprire che è necessario un interprete e che si è creato un problema completo di Turing.

Per non parlare degli altri aspetti dinamici di JavaScript che sfidano completamente l'analisi statica, come il recupero delle proprietà mediante notazione a parentesi quadre, il temuto eval o le stringhe in timeout o gli attributi del gestore eventi.

Penso che sia un po 'un antipasto davvero. Probabilmente stai meglio monitorare manualmente le dipendenze, ma semplificarlo raggruppando le funzioni correlate in moduli che saranno la tua unità di base del monitoraggio delle dipendenze. OK, inserirai qualche altra funzione di cui hai bisogno tecnicamente, ma si spera non troppo.

È anche una buona idea assegnare uno spazio ai nomi per ogni modulo, quindi è molto chiaro dove ogni chiamata sta andando, rendendo più facile mantenere le dipendenze in controllo manualmente (ad esempio con un commento // uses: ThisModule, ThatModule in alto).

Poiché le estensioni dei prototipi incorporati sono più difficili da tenere traccia di, mantenerle al minimo. Estendendo ad es. Array per includere i metodi ECMAScript della Quinta Edizione (come indexOf) sui browser che non li hanno già è una buona cosa da fare come una correzione di base che tutti gli script useranno. L'aggiunta di funzionalità arbitrarie completamente nuove ai prototipi esistenti è discutibile.

+0

Grazie per la risposta ... Prenderò una strada diversa allora. – user386508

8

Come già suggerito da @bobince, fare un'analisi statica su un programma JavaScript è un problema quasi impossibile da violare. Google Closure compiler lo fa in una certa misura, ma poi si basa anche sulla guida esterna dei commenti di JSDoc.

Avevo un similar problem di trovare l'ordine in cui i file JS devono essere concatenati in un progetto precedente e poiché c'erano molti file JS, l'aggiornamento manuale dell'ordine di inclusione sembrava troppo noioso. Invece, mi sono attenuto a certe convenzioni su ciò che costituisce una dipendenza per i miei scopi, basandomi su questo e usando il semplice regexp :) Sono stato in grado di generare l'ordine di inclusione corretto.

La soluzione utilizzava un algoritmo topological sort per generare un dependency graph che elencava quindi i file nell'ordine in cui dovrebbero essere inclusi per soddisfare tutte le dipendenze. Dato che ogni file era fondamentalmente una pseudo-classe usando la sintassi MooTools, c'erano solo 3 modi in cui le dipendenze potevano essere create per la mia situazione.

  1. Quando una classe è estesa ad un'altra classe.
  2. Quando una classe implementava un'altra classe.
  3. Quando una classe creava un'istanza di un oggetto di un'altra classe utilizzando la parola chiave new.

Si trattava di una soluzione semplice e decisamente interrotta per uso generale, ma mi è servita bene. Se sei interessato alla soluzione, puoi vedere lo code here - è in Ruby.

Se le dipendenze sono più complessi, allora forse si potrebbe elencare manualmente le dipendenze in ogni JS file stesso utilizzando i commenti e una sintassi homegrown come ad esempio:

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

quindi leggere ogni file JS, analizza la richiede commenti e costruisci un grafico delle dipendenze che ti darà l'ordine di inclusione di cui hai bisogno.

5

Sarebbe bello avere uno strumento in grado di rilevare automaticamente tali dipendenze e scegliere il modo in cui vengono caricate. Le migliori soluzioni oggi sono un po 'più grosse. Ho creato un responsabile delle dipendenze per le mie particolari esigenze che voglio aggiungere all'elenco (Pyramid Dependency Manager). Ha alcune caratteristiche chiave che risolvono alcuni casi d'uso unici.

  1. Maniglie altri file (tra cui l'inserimento di html per le viste ... sì, è possibile separare le vostre opinioni durante lo sviluppo)
  2. unisce i file per voi in javascript quando si è pronti per il rilascio (non è necessario installare esterni strumenti)
  3. Ha una inclusione generica per tutte le pagine html. Devi solo aggiornare un file quando una dipendenza viene aggiunta, rimossa, rinominata, ecc.

Alcuni esempi di codice per mostrare come funziona durante lo sviluppo.

File: 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'] 
}); 

file 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> 

Essa richiede di mantenere un singolo file per gestire le dipendenze.Sto pensando di creare un programma in grado di generare automaticamente il file del caricatore per te in base all'inserimento nell'intestazione, ma poiché gestisce molti tipi diversi di dipendenze, mantenerli in un unico file potrebbe effettivamente essere migliore.

1

JSAnalyse utilizza l'analisi statica del codice per rilevare le dipendenze tra i file JavaScript: si http://jsanalyse.codeplex.com/

Consente inoltre di definire le dipendenze consentiti e per assicurarsi che durante la costruzione, per esempio. Ovviamente, non può rilevare tutte le dipendenze perché javascript è un linguaggio di interpretariato dinamico che non è sicuro per il tipo, come già accennato. Ma almeno ti rende consapevole del tuo grafico di dipendenza JavaScript e ti aiuta a tenerlo sotto controllo.

10

Hai provato a utilizzare un gestore delle dipendenze come RequireJS o LabJS? Ho notato che nessuno li ha menzionati in questo thread.

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

All'interno main.js, è possibile utilizzare require() per caricare qualsiasi altro script si necessario eseguire:

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". 
}); 

si può annidare quelle dipendenze così, helper/util può richiedere alcuni altri file dentro di sé.

0

ho scritto uno strumento per fare qualcosa di simile: http://github.com/damonsmith/js-class-loader

E 'più utile se si dispone di una webapp Java e si struttura il codice JS in stile Java. In tal caso, è in grado di rilevare tutte le dipendenze del codice e raggrupparle, con supporto per entrambe le dipendenze di runtime e parse-time.

Problemi correlati