2013-03-30 16 views
10

Questo potrebbe essere già stato chiesto un sacco di volte, e ho cercato in giro SO ma finora tutte le risposte che ho letto non sono esattamente quello che sto cercando.Modello di disegno JavaScript letterale o modulare

Sto lavorando a un sito Web con elementi DOM moderati che mostrano/nascondono, alcune chiamate AJAX e probabilmente qualcos'altro. Così sarò avendo due file principali di script (standard HTML5 Boilerplate)

plugins.js // third party plugins here 
site.js // all my site specific code here 

precedenza che sto usando modello oggetto di design letterale, quindi il mio site.js è qualcosa di simile:

var site = { 
    version: '0.1', 
    init: function() { 
    site.registerEvents(); 
    }, 
    registerEvents: function() { 
    $('.back-to-top').on('click', site.scrollToTop); 
    }, 
    scrollToTop: function() { 
    $('body').animate({scrollTop: 0}, 400); 
    } 
}; 

$(function() { 
    site.init(); 
}); 

Fin qui tutto buono, è ben leggibile, tutti i metodi sono pubblici (mi piace un po ', dato che posso testarli direttamente tramite Chrome Dev Tools se necessario). Tuttavia, ho intenzione di separare alcune delle funzionalità del sito in stile più modulare, quindi voglio avere qualcosa di simile sotto il codice di cui sopra (o in file separati):

site.auth = { 
    init: function() { 
    site.auth.doms.loginButton.on('click', site.auth.events.onLoginButtonClicked); 
    }, 
    doms: { 
    loginButton: $('.login'), 
    registerButton: $('.register') 
    }, 
    events: { 
    onLoginButtonClicked: function() { 
    } 
    }, 
    fbLogin: function() { 
    } 
}; 

site.dashboard = { 
}; 

site.quiz = { 
}; 

// more modules 

Come si può vedere, molto è leggibile. Tuttavia c'è uno svantaggio evidente, che è che devo scrivere il codice come site.auth.doms.loginButton e site.auth.events.onLoginButtonClicked. Improvvisamente diventa difficile da leggere, e crescerà solo più a lungo, più diventa complessa la funzionalità. Poi ho provato il modello modulare:

var site = (function() { 
    function init() { 
    $('.back-to-top').on('click', scrollToTop); 
    site.auth.init(); 
    } 

    function scrollToTop() { 
    $('body').animate({scrollTop: 0}, 400); 
    } 

    return { 
    init: init 
    } 
})(); 

site.auth = (function() { 
    var doms = { 
    loginButton: $('.login'), 
    registerButton: $('.register') 
    }; 

    function init() { 
    doms.loginButton.on('click', onLoginButtonClicked); 
    } 

    function onLoginButtonClicked() { 

    } 

    return { 
    init: init 
    } 
})(); 

// more modules 

Come si può vedere, i nomi lunghi non ci sono più, ma poi penso che devo a init tutti gli altri moduli nella funzione site.init() per la costruzione di tutti? Quindi devo ricordare di restituire le funzioni che devono essere accessibili da altri moduli. Entrambi sono a posto, credo che sia un po 'complicato, ma nel complesso, sto lavorando su un flusso di lavoro migliore con pattern modulari?

+0

E riguardo 'this.doms.loginButton.on ('clic', this.events.onLoginButtonClicked);'? – Bergi

+0

Potrebbe funzionare, non posso credere di non averlo provato. Ma è ancora abbastanza lungo da scrivere rispetto al modello modulare. – Henson

risposta

15

La risposta corretta, qui, ovviamente, è: "dipende".

Se sei totalmente a posto con tutti i dati e tutti i metodi, perché ogni sezione del tuo sito è pubblica al 100%, quindi è sufficiente utilizzare un singolo letterale (o più letterali), con gli oggetti nidificati, se lo desideri, partendo dal presupposto che puoi evitare che si trasformi in una gigantesca sfera di codice.

Se si desidera qualsiasi tipo di stato privato, che ha qualsiasi tipo di persistenza (cioè: non si reimposta ogni volta che si esegue una funzione), il modulo rivelatore è ottimo.

Detto questo:
Non è necessario che il modulo di rivelazione abbia un metodo .init.
Se il modulo può essere autonomo, concentrati solo sull'esportazione di ciò che ti piacerebbe rendere pubblico.

A tal fine, quando sto scrivendo il codice che potrebbe essere visualizzato da una squadra, in seguito, mi trovo a creare un oggetto public_interface ea restituirlo (la versione con nome dell'oggetto anonimo restituito).

Il vantaggio di questo è minimo, tranne per aggiungere la comprensione che tutto ciò che deve essere reso pubblico deve essere aggiunto all'interfaccia.

Il modo in cui si sta attualmente utilizzando è:

var module = (function() { /* ... */ return {}; }()); 

module.submodule = (function() { /*...*/ return {}; }()); 

non è migliore o peggiore di letterali, perché si può altrettanto facilmente fare questo:

var module = { 
    a : "", 
    method : function() {}, 
    meta : { } 
}; 

module.submodule = { 
    a : "", 
    method : function() {}, 
    meta : { } 
}; 

fino a colpire qualcosa che doesn lavora per te, lavora con ciò che soddisfa i tuoi bisogni.

Personalmente, io di solito costruire qualsiasi oggetti dati solo come letterali: config-oggetti, oggetti che arrivano da altre connessioni, ecc ...

Qualsiasi oggetto sporco semplice, che richiede forse uno o due metodi, e può essere costruito nidificando solo uno o due livelli in profondità, potrei anche costruire letteralmente (purché non richieda l'inizializzazione).

// ex: 
var rectangle = { 
    width : 12, 
    height : 24, 
    area : 0, 
    perimeter : 0, 
    init_area : function() { this.area = this.width * this.height; return this; }, // buh... 
    init_perimeter : function() { this.perimeter = (this.width * 2) + (this.height * 2); return this; } // double-buh... 
}.init_area().init_perimeter(); 

se avevo bisogno di molti di questi, forse farei un costruttore.
Ma se ho sempre e solo bisogno di uno di qualcosa di unico come questo, non mi risparmiare qualche mal di testa di fare proprio qualcosa di simile:

var rectangle = (function (width, height) { 
    var public_interface = { 
     width : width, 
     height : height, 
     area : width * height, 
     perimeter : (2 * width) + (2 * height) 
    }; 
    return public_interface; 
}(12, 24)); 

se ci fossero calcoli più avanzati richiesto, ho potuto mantenere qualsiasi extra vars private, e lavorarci sopra.
Se dovessi avere dati sensibili all'interno di un oggetto e funzioni per lavorare su quei dati, allora potrei avere funzioni pubbliche che chiamano quelle funzioni private e restituiscono risultati, piuttosto che fornire accesso.

Inoltre, se si effettua il refactoring del mio codice e si decide di rinominare rectangle a un certo punto, quindi tutte le funzioni nidificate 3 o più in profondità, che si riferiscono a rectangle, dovranno essere modificate.
Anche in questo caso, se si sta strutturando i vostri metodi in modo che essi non hanno bisogno di chiedere direttamente per qualsiasi oggetto che è più in alto rispetto this, allora non avrete questo problema ...

... ma se si dispone di un'interfaccia che assomiglia:

MyApp.myServices.webService.send(); 

e sta pensando di trovare:

MyApp.appData.user.tokens.latest; // where personally, I might leave tokens in a closure 

Se si modifica la struttura del modulo appData, si sta andando ad avere tutti i tipi di errori in il tuo modulo webService , fino a trovare ogni riferimento al vecchio formato e rinominarli tutti.

+0

Quindi, in ogni caso, quindi non ci sono veri pro/contro tra l'uso di Object Literal vs Revealing Modular oltre alla privacy e alle preferenze personali? – Henson

+1

@henson E riferimento interno, all'interno di un oggetto letterale, a proprietà che si basano su altre proprietà, che devono essere calcolate dopo il fatto o che creano chiusure all'interno dei metodi dell'oggetto, che richiede che le proprietà preesistono nel momento in cui provare a creare la funzione ... Tutto si riduce alla complessità. Se riesci a gestire tutta la costruzione degli oggetti in modo che tutti i tuoi oggetti di grandi dimensioni siano solo dati grezzi, allora i letterali degli oggetti vanno bene. Se hai bisogno di chiusure o hai bisogno di pre-calcolare in base ai membri preesistenti dell'oggetto, allora i letterali sono un disordine di '{} .init()' – Norguard

Problemi correlati