Questo modo di definire gli oggetti JS ha uno scopo?


87

Sto mantenendo un po 'di codice legacy e ho notato che viene utilizzato il seguente modello per la definizione degli oggetti:

var MyObject = {};

(function (root) {

    root.myFunction = function (foo) {
        //do something
    };

})(MyObject);

C'è uno scopo in questo? È equivalente a fare solo quanto segue?

var MyObject = {

    myFunction : function (foo) {
        //do something
    };

};

Non ho intenzione di intraprendere una missione sacra per rifattorizzare l'intera base di codice secondo i miei gusti, ma mi piacerebbe davvero capire il motivo dietro quel modo indiretto di definire gli oggetti.

Grazie!


1
Nel tuo esempio esatto non c'è differenza. Se lo espandi, potrebbe esserci una differenza, ma ci saranno anche approcci diversi che entreranno in gioco.
Travis J

Non fa differenza, gli oggetti vengono passati come copia di un riferimento per così dire, quindi anche quando si definisce la myFunction all'interno dell'IIFE, è comunque accessibile al di fuori di esso.
adeneo

1
@adeneo Non per questo esempio, myFunctionpotrebbe utilizzare alcune variabili definite al di fuori di sé che non sarebbero accessibili dall'esterno. Vedi la mia risposta
Juan Mendes


Risposte:


116

Si chiama pattern modulo http://toddmotto.com/mastering-the-module-pattern/

Il motivo principale è creare metodi e variabili veramente privati. Nel tuo caso, non è significativo perché non nasconde alcun dettaglio di implementazione.

Ecco un esempio in cui ha senso usare il pattern del modulo.

var MyNameSpace = {};

(function(ns){
    // The value variable is hidden from the outside world
    var value = 0;

    // So is this function
    function adder(num) {
       return num + 1;
    }

    ns.getNext = function () {
       return value = adder(value);
    }
})(MyNameSpace);

var id = MyNameSpace.getNext(); // 1
var otherId = MyNameSpace.getNext(); // 2
var otherId = MyNameSpace.getNext(); // 3

Mentre se usassi solo un oggetto semplice addere valuediventeresti pubblico

var MyNameSpace = {
    value: 0,
    adder: function(num) {
       return num + 1;
    },
    getNext: function() {
       return this.value = this.adder(this.value);
    }
}

E potresti romperlo facendo cose come

MyNameSpace.getNext(); // 1
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 1 again
delete MyNameSpace.adder;
MyNameSpace.getNext(); // error undefined is not a function

Ma con la versione del modulo

MyNameSpace.getNext(); // 1
 // Is not affecting the internal value, it's creating a new property
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 2, yessss
// Is not deleting anything
delete MyNameSpace.adder;
MyNameSpace.getNext(); // no problemo, outputs 3

2
Questo non risponde davvero alla domanda su quale sia la differenza tra le due alternative?

20
@torazaburo L'esempio dell'OP non è stato un buon esempio, ho fornito un esempio reale che mostra quando usare il pattern del modulo.
Juan Mendes

Questo ns.getNext: function () {non verrà compilato.
punund

Lo avrei fatto, se fossi sicuro di come risolverlo. Pensavo ci fosse qualche costrutto da prevenire delete MyNameSpace.getNext.
punund

2
@punund JS non ha un compilatore, ha un interprete:)
frogatto

22

Lo scopo è limitare l' accessibilità delle funzioni all'interno della chiusura per impedire che altri script eseguano codice su di essa. Avvolgendolo attorno a una chiusura si ridefinisce l' ambito di esecuzione di tutto il codice all'interno della chiusura e si crea efficacemente un ambito privato. Vedi questo articolo per maggiori informazioni:

http://lupomontero.com/using-javascript-closures-to-create-private-scopes/

Dall'articolo:

Uno dei problemi più noti in JavaScript è la sua dipendenza da uno scope globale, il che significa fondamentalmente che tutte le variabili dichiarate al di fuori di una funzione vivono nello stesso spazio dei nomi: il sinistro oggetto finestra. A causa della natura delle pagine web, molti script da fonti diverse possono (e verranno) eseguiti sulla stessa pagina condividendo un ambito globale comune e questa può essere davvero una brutta cosa in quanto può portare a collisioni di nomi (variabili con lo stesso nome essere sovrascritti) e problemi di sicurezza. Per ridurre al minimo il problema, possiamo utilizzare le potenti chiusure di JavaScript per creare ambiti privati ​​in cui possiamo essere sicuri che le nostre variabili siano invisibili ad altri script sulla pagina.



Codice:

var MyObject = {};

(function (root) {
    function myPrivateFunction() {
       return "I can only be called from within the closure";
    }

    root.myFunction = function (foo) {
        //do something
    };    

    myPrivateFunction(); // returns "I can only be called from within the closure"

})(MyObject);


myPrivateFunction(); // throws error - undefined is not a function

1
myFunctionnon è nell'ambito globale nella seconda versione. Lo scopo è in realtà fornire un ambito in cui definire le funzioni ausiliarie interne.
Barmar

myFunctionè nell'ambito globale perché è definito all'interno dell'oggetto globale myObject. Nella seconda versione può essere eseguito qualsiasi altro codice dell'applicazione myFunction. Nella prima versione solo il codice all'interno della chiusura ha accesso amyFunction
Jonathan Crowe

No, myFunctionpuò essere eseguito solo come MyObject.myFunction(), come la prima versione.
Barmar

@JonathanCrowe L'esempio dell'OP non è un buon esempio, sta esponendo tutto all'interno del modulo, quindi sì, sta diventando accessibile all'esterno. Vedi la mia risposta per un utile modulo caso
Juan Mendes

@JuanMendes buon punto, l'esempio di OP non è un ottimo uso del modello del modulo
Jonathan Crowe

6

vantaggi:

  1. mantiene le variabili in ambito privato.

  2. puoi estendere la funzionalità dell'oggetto esistente.

  3. le prestazioni sono aumentate.

Penso che i tre semplici punti di cui sopra siano appena sufficienti per seguire queste regole. E per mantenerlo semplice, non è altro che scrivere funzioni interne.


6

Nel caso particolare che mostri, non c'è differenza significativa, in termini di funzionalità o visibilità.

È probabile che il programmatore originale abbia adottato questo approccio come una sorta di modello che gli consente di definire variabili private che potrebbero essere utilizzate nella definizione di cose come myFunction:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;   // <-- private variable
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

Ciò evita di calcolare seconds_per_dayogni volta che la funzione viene chiamata, pur impedendo che inquini l'ambito globale.

Tuttavia, non c'è niente di essenzialmente diverso da quello e basta dire

var MyObject = function() {
    var seconds_per_day = 24 * 60 * 60;
    return {
        myFunction: function(foo) {
            return seconds_per_day;
        }
    };
}();

Il programmatore originale potrebbe aver preferito essere in grado di aggiungere funzioni all'oggetto usando la sintassi dichiarativa di root.myFunction = function, piuttosto che la sintassi oggetto / proprietà di myFunction: function. Ma questa differenza è principalmente una questione di preferenza.

Tuttavia, la struttura adottata dal programmatore originale ha il vantaggio che proprietà / metodi possono essere facilmente aggiunti altrove nel codice:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

(function(root) {
    var another_private_variable = Math.pi;
    root.myFunction2 = function(bar) { };
})(MyObject);

In conclusione, non è necessario adottare questo approccio se non è necessario, ma non è nemmeno necessario modificarlo, poiché funziona perfettamente e presenta alcuni vantaggi.


6
  1. Il primo pattern può essere usato come un modulo che prende un oggetto e lo restituisce con alcune modifiche. In altre parole, è possibile definire tali moduli come segue.

    var module = function (root) {
        root.myFunction = function (foo) {
            //do something
        };
    }
    

    E usalo come:

    var obj = {};
    module(obj);
    

    Quindi un vantaggio potrebbe essere la riutilizzabilità di questo modulo per usi successivi.


  1. Nel primo modello, puoi definire un ambito privato per memorizzare le tue cose private come proprietà e metodi privati. Ad esempio, considera questo snippet:

    (function (root) {
    
        // A private property
        var factor = 3;
    
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(MyObject);
    

  1. Questo modello può essere utilizzato per aggiungere un metodo o una proprietà a tutti i tipi di oggetti come array, valori letterali oggetto, funzioni.

    function sum(a, b) {
        return a + b;
    }
    
    (function (root) {
        // A private property
        var factor = 3;
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(sum);
    
    console.log(sum(1, 2)); // 3
    console.log(sum.multiply(4)); // 12
    

Secondo me il vantaggio principale potrebbe essere il secondo (creare un ambito privato)


5

Questo modello fornisce un ambito in cui è possibile definire funzioni di supporto che non sono visibili nell'ambito globale:

(function (root) {

    function doFoo() { ... };

    root.myFunction = function (foo) {
        //do something
        doFoo();
        //do something else
    };

})(MyObject);

doFoo è locale per la funzione anonima, non può essere referenziato dall'esterno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.