2013-02-08 5 views
5

Con la mia comprensione limitata di RequireJS e Node.js (più JavaScript in generale), di solito guardo all'origine di alcune librerie JavaScript note. Ogni volta che vedo qualcosa di simile:Perché questo codice JavaScript (modello di modulo per RequireJS e Node.js) funziona?

(// Wrapping 
    function (root, factory) { 
     if (typeof exports === 'object') { // Node.js 

      var underscore = require('underscore'); 
      var backbone = require('backbone'); 

      module.exports = factory(underscore, backbone); 

     } else if (typeof define === 'function' && define.amd) { // Require.JS 

      define(['underscore', 'backbone'], factory); 

     } 
    }(this, function (_, Backbone) { // Factory function, the implementation 
     "option strict"; 

     function Foo() {} 

     return Foo; // Export the constructor 
    }) 
); // Wrapping 

Cosa posso capire (si spera):

  • La funzione anonima che avvolge il codice viene eseguito automaticamente quando lo script è uncluded in un tag <script>
  • Questo codice funziona con entrambi i requisiti RequireJS e Node.js (if all'inizio); il risultato della funzione factory è assegnato a module.exports (Node.js) o utilizzato come argomento della funzione define (RequireJS).

Q1: come funziona questo codice senza RequireJS e Node.js? I controlli if e else if non funzionerebbero, la funzione factory non viene mai eseguita e gli script restituiscono nothig.

Q2: qual è lo scopo di passare this come argomento root? Non è mai usato

+1

Sei sicuro che funzioni senza RequireJS o Node.js? La mia comprensione limitata di JavaScript (e questo forse errato) [JSFiddle] (http://jsfiddle.net/aM3ZT/) mi fa pensare che non puoi accedere a Foo() –

+0

@nekman Ahh, vedo che presuppone almeno Backbone è disponibile . È intelligente –

+0

@JasonSperske non è sicuro al 100%, ma guarda la risposta di nekman ... – gremo

risposta

5

In realtà penso che il codice tagliato nella domanda non funzionerà con i globali del browser. Il modello utilizzato in questo taglio è chiamato UMD - Universal Module Definition. In realtà ci sono molte varianti di questo modello, è possibile sfogliare altri esempi su https://github.com/umdjs/umd

Per quanto riguarda le domande:

Q1 Questo frammento non funziona nei browser, senza RequireJS o qualsiasi altro caricatore di AMD, per ovvi motivi - ci sono solo due controlli - per il NodeJS e definire la funzione, quindi senza la libreria AMD la funzione di fabbrica non verrà chiamata.

Per rendere la funzione di fabbrica chiamato basta aggiungere un'altra condizione per globali del browser

if (typeof exports === 'object') { // Node.js 
    var underscore = require('underscore'); 
    var backbone = require('backbone'); 
    module.exports = factory(underscore, backbone); 

} else if (typeof define === 'function' && define.amd) { // Require.JS 
    define(['underscore', 'backbone'], factory); 
} else { 
    // Browser globals 
    factory(root._, root.Backbone); 
} 

Nota che abbiamo usato oggetto radice passato alla funzione wrapper e come nekman rilevare che sarà impostato su window in ambiente del browser, quindi passiamo semplicemente gli oggetti globali definiti su quella finestra alla fabbrica, questi oggetti sono generalmente definiti da altri tag script nella pagina. Spero che questo risponda alla tua seconda domanda.

+0

+1, grazie per il collegamento UMD! Come ho detto sto imparando a conoscere JavaScript e quello che non riesco a capire è come si possa accedere al costruttore 'Foo' quando non si utilizza Node.js o Require.JS. L'unico modo è scrivere 'root.Foo = factory (root._, root.Backbone)', giusto? – gremo

+0

In realtà ho trovato la risposta al mio commento qui: https://github.com/umdjs/umd/blob/master/returnExports.js – gremo

+0

Sì, questo è il modo per farlo, basta restituire il tuo oggetto (' Foo') e se siamo nel browser dovremmo aggiungerlo a 'root' (che è la finestra) in modo che altri moduli possano vederlo. –

2

Q1: Se sia il if e else if fallisce, l'unica cosa da supporre è che underscore e Backbone viene caricato da un tag <script>. Per qualche tempo fa, ho added a commit allo Backbone.localStorage plugin che ha fatto la stessa ipotesi.

Q2: Il this punterà alla "oggetto globale" (window in un ambiente browser e global in un ambiente Node.js). Nel tuo caso, non viene utilizzato e non è necessario passarlo. Il solo factory sarebbe sufficiente.

+1

Per ** Q1 **: Non riesco a capire le linee che vengono eseguite quando 'if' e' else if' falliscono (cioè il browser). Puoi spiegarlo? Come posso accedere a 'Foo()'? – gremo

+1

Sì, questo è correttamente corretto! Se entrambi falliscono, allora dovrebbe esserci un: 'else {factory (_, Backbone); } 'per accedere alla fabbrica e' Foo'. – nekman

+0

Anche con 'return factory (_, Backbone)' Non riesco a capire come, quando si include lo script con il tag '