Ich habe gehört, dass globale Variablen schlecht sind. Welche alternative Lösung sollte ich verwenden?

72

Ich habe überall gelesen, dass globale Variablen schlecht sind und Alternativen verwendet werden sollten. Welche Lösung sollte ich speziell in Javascript wählen?

Ich denke an eine Funktion, die beim Einspeisen von zwei Argumenten ( function globalVariables(Variable,Value)) prüft, ob Variable in einem lokalen Array vorhanden ist und ob sie ihren Wert auf Valueelse setzt Variableund Valueangehängt wird. Wenn die Funktion ohne Argumente ( function globalVariables()) aufgerufen wird , gibt sie das Array zurück. Wenn die Funktion mit nur einem Argument ( function globalVariables(Variable)) ausgelöst wird, gibt sie möglicherweise den Wert von Variableim Array zurück.

Was denken Sie? Ich würde gerne Ihre alternativen Lösungen und Argumente für die Verwendung globaler Variablen hören.

Wie würden Sie verwenden globalVariables();

function append(){
    globalVariables("variable1","value1"); //globalVariables() would append variable1 to it's local array.
};

function retrieve(){
    var localVariable1 = globalVariables("variable1"); //globalVariables() would return "value1".
};

function retrieveAll(){
    var localVariable1 = globalVariables(); //globalVariables() would return the globalVariable()'s entire, local [persistently stored between calls] array.
};

function set(){
    globalVariables("variable1","value2"); //globalVariables() would set variable1 to "value2".
};

Ist das übrigens ein Singleton-Muster ?

In diesem speziellen Szenario kann eine Funktion eine Variable zu einem bestimmten Zeitpunkt festlegen, und viel später muss eine andere Funktion, möglicherweise wenn ein Benutzer ein Formular sendet, diese Variable abrufen. Daher konnte die erste Funktion die Variable nicht als Argument an die spätere Funktion übergeben, da sie niemals von der ersten aufgerufen werden würde.

Vielen Dank, ich danke Ihnen für Ihre Hilfe!

Jonathon Oates
quelle
11
Ich glaube nicht, dass Sie ganz verstehen, warum sie schlecht sind
Yacoby
1
@Yacoby: Ich glaube schon [Lees Antwort erklärt viel], aber wenn Sie sich anders fühlen, dann gehen Sie bitte näher darauf ein. Welche Lösung sollte ich in diesem Szenario verwenden?
Jonathon Oates
Ich muss hier @Yacoby zustimmen. Sie erfinden globale Variablen in Ihrem Beispiel neu und kehren so zu Schritt 1 von "Globale Variablen sind schlecht" zurück.
Patrick
4
OK, danke Yacoby und Patrick, aber wie kann ich eine Variable zwischen mehreren Funktionen beibehalten, die sich nicht gegenseitig aufrufen, sodass keine Argumente gesendet werden können?
Jonathon Oates
1
Soll ich den Variablenwert stattdessen als Alternative in einem Cookie speichern?
Jonathon Oates

Antworten:

138

Der Hauptgrund, warum globale Variablen in Javascript nicht empfohlen werden, liegt darin, dass in Javascript alle Codes einen einzigen globalen Namespace gemeinsam haben und auch Javascript globale Variablen impliziert hat, d. H. Variablen, die nicht explizit im lokalen Bereich deklariert sind, werden automatisch zum globalen Namespace hinzugefügt. Wenn Sie sich zu sehr auf globale Variablen verlassen, kann dies zu Kollisionen zwischen verschiedenen Skripten auf derselben Seite führen (lesen Sie die Artikel von Douglas Crockford ).

Eine Möglichkeit, globale Variablen zu reduzieren, besteht darin, das YUI-Modulmuster zu verwenden . Die Grundidee besteht darin, Ihren gesamten Code in eine Funktion zu verpacken, die ein Objekt zurückgibt, das Funktionen enthält, auf die außerhalb Ihres Moduls zugegriffen werden muss, und den Rückgabewert einer einzelnen globalen Variablen zuzuweisen.

var FOO = (function() {
    var my_var = 10; //shared variable available only inside your module

    function bar() { // this function not available outside your module
        alert(my_var); // this function can access my_var
    }

    return {
        a_func: function() {
            alert(my_var); // this function can access my_var
        },
        b_func: function() {
            alert(my_var); // this function can also access my_var
        }
    };

})();

Verwenden Sie jetzt, um Funktionen in Ihrem Modul an anderer Stelle zu verwenden FOO.a_func(). Auf diese Weise müssen Sie nur den Namen von ändern, um globale Namespace-Konflikte zu lösen FOO.

z33m
quelle
2
Das ist cool, es wird eine komplette Umschreibung meines Codes erfordern, aber ich bin fertig damit, großartig, Antwort akzeptiert.
Jonathon Oates
7
heh heh - diese letzten beiden Klammern sind ein Gotcha! ohne sie müssten Sie sich beziehen FOO().a_func(). Ah, es macht jetzt Sinn!
ErichBSchulz
3
Kollisionen zu benennen ist also der einzige Grund? oder gibt es auch Nachteile der Speichernutzung?
Bhavya_w
1
Ist diese Praxis nicht grundsätzlich auf Bibliotheken oder anderen wiederverwendbaren Code ausgerichtet? Wenn Sie nativen Code für eine Web-App schreiben, die normalerweise nicht anderweitig verwendet wird - und davon ausgehen, dass Sie sie nicht schlecht benennen oder schlechte Bibliotheken verwenden, die den globalen Namespace füllen -, was bringt das in Ihrer Web-App? Nur irgendwie sauberer, auf Kosten des Einrückens Ihres gesamten Codes in jedes Modul?
Bryc
1
Es ist interessant, dass hier in vanilla js der Funktionsaufruf außerhalb der Klammern liegt, die die anonyme Funktion umgeben. Aber in allen Modulmustern von node.js, die ich gesehen habe, befindet sich der Funktionsaufruf in diesen Klammern. Macht das einen Unterschied? Und wenn ja, was ist das?
krb686
43

Semantik mein Junge. Semantik.

Beginnen Sie mit einem globalen: myApp = {}; Alles sollte darin sein. Die einzige Ausnahme wäre Ihre AJAX-Bibliothek (es gibt einige extreme Ausnahmen wie das Arbeiten mit JSONP-Rückrufen).

In myApp sollten nur sehr wenige Eigenschaften vorhanden sein. Sie möchten Ihre Anwendungseigenschaften in Containern wie config oder settings speichern.

myApp = {
    config:{
        prop:1
    },
    settings:{
        prop:2
    },
    widgets:{
        List: function(props){},
        Item: function(props){}
    }
}

Dann haben Sie möglicherweise mehr Eigenschaften in niedrigeren Modulen, Komponenten, Singletons und Klassenkonstruktoren (Widgets).

Dieses Setup bietet Ihnen den zusätzlichen Vorteil, dass Sie von jedem anderen Ort aus auf jede Eigenschaft zugreifen können, da Sie sie mit myApp global abrufen können. Sie sollten jedoch "dies" verwenden, wann immer dies möglich ist, da die Suche schneller ist. Und stellen Sie die Eigenschaft einfach direkt ein, kümmern Sie sich nicht um das Pseudo-Getter / Setter-Zeug. Wenn Sie wirklich einen Getter / Setter benötigen, codieren Sie ihn für diese spezielle Verwendung.

Der Grund, warum Ihr Beispiel nicht funktioniert, ist, dass es zu allgemein ist und Sie nach einer Ausrede suchen, um im globalen Raum zu arbeiten.

Und seien Sie nicht schlau mit privaten Variablen. Sie sind auch schlecht: http://clubajax.org/javascript-private-variables-are-evil/

mwilcox
quelle
2
toller Kommentar mwilcox. Ich habe diesen Beitrag genutzt, um die anderen hier bei der Arbeit davon zu überzeugen.
Chris
So wird es gemacht. Eine einzige globale mit all Ihren Variablen, auf die unbedingt global zugegriffen werden muss. +1
welbornio
Kennt jemand ein Tool, mit dem eine große App mithilfe des globalen Bereichs in diese Art konvertiert werden kann?
Weiß nicht
9

Der globale Staat verursacht in mehreren Bereichen Probleme. Eine davon ist die Wiederverwendung von Code. Wenn Sie auf einen globalen Status zugreifen, bedeutet dies, dass sich die Komponente ihrer Umgebung bewusst sein muss (etwas außerhalb von sich selbst). Sie sollten dies so weit wie möglich vermeiden, da dies die Komponente unvorhersehbar macht.

Angenommen, ich habe ein Objekt, das auf Ihre globalVariables-Funktion zugreift, und ich möchte es auf einer anderen Seite verwenden. Woher weiß ich, wie ich das globalVariables-Objekt definiere oder wie ich es überhaupt definiere? Wenn Sie die Informationen jedoch an einen Konstruktor oder als Argument an eine Funktion übergeben können, kann ich leicht feststellen, was für das Objekt erforderlich ist.

Auch wenn Sie auf den globalen Bereich zugreifen oder ihn ändern, besteht die Gefahr, dass andere Objekte davon betroffen sind. Aus diesem Grund verwenden Bibliotheken wie jquery im globalen Bereich nur einen einzigen Namen (den geringstmöglichen). Dies verringert die Möglichkeit von Konflikten mit anderen Bibliotheken. Mit anderen Worten, der globale Geltungsbereich liegt außerhalb Ihrer Kontrolle und ist daher gefährlich.

Lee
quelle
3

Die Verwendung globaler Variablen ist im Allgemeinen eine schlechte Praxis, unabhängig von der Sprache der Wahl. Sie dürfen nicht einmal (leicht) im strengen Modus verwendet werden , was ich sehr empfehlen kann.

Betrachten Sie diesen Code, den ich gefunden habe:

if (typeof session != 'undefined' && !data.cart.request_status)
  data.input_definitions.passengers =
    inflate_passenger(session, data.input_definitions.passengers);

Ich musste mich umdrehen und einen Felow-Programmierer fragen, woher diese sessionVariable stammt, da keine Codesuche angezeigt wurde, wo sie eingestellt war.

Ich stellte fest, dass ein anderes Paket der Firma die globale Variable setzt. Code ist wie ein Witz: Wenn Sie es erklären müssen, ist es wahrscheinlich nicht so gut.

Problemumgehung mit ES6:

Wenn Sie bei Node das gewünschte Material verwenden importoder requirein den lexikalischen Bereich bringen, lassen Sie nicht zu, dass Personen Ihre globale Umgebung berühren, ohne dass Sie es wissen.

import {Sesssion} from 'api-core';
const Session = require('api-core').session;

Wenn Sie am Frontend Code für den Browser bereitstellen, können Sie diesen nur verwenden, importwenn Sie Ihren ES6-Code mit Babel transpilieren .

Beispiel für das Transpilieren mit Gulp.js:

// $ npm install --save-dev gulp-babel babel-preset-es2015

// gulpfile.js
const gulp  = require('gulp');
const babel = require('gulp-babel');

gulp.task('transpile', () => {
  return gulp.src('src/app.js')
    .pipe(babel({presets: ['es2015']}))
    .pipe(gulp.dest('dist'));
});

// $ gulp transpile

Legacy-Problemumgehung:

Wenn die Verwendung von ES6-Funktionen keine Option ist, besteht die einzige Problemumgehung für die Verwendung einer Reihe globaler Variablen darin, nur eine zu verwenden, und hoffen:

// scripts/app.js
var MyApp = {
  globals: {
    foo: "bar",
    fizz: "buzz"
  }
};
Nicooga
quelle
2

Das Problem bei Ihrer Lösung ist, dass Sie den Code nur schwerer verstehen, während Sie alle Nachteile globaler Variablen beibehalten. Die Seite, auf die Sie verlinkt haben, behandelt die Probleme. Das einzige Problem, das Ihre vorgeschlagene Lösung wirklich löst, ist die Verschmutzung des Namespaces, aber auf Kosten der Tatsache, dass Sie nicht sehen können, welche globalen Variablen so einfach deklariert werden, wie die Deklaration ein Funktionsaufruf ist.

Die Lösung besteht darin, Code ohne globale Variablen zu schreiben. Wenn eine Funktion einen Wert benötigt, übergeben Sie ihn als Argument.

Yacoby
quelle
2
... und wenn ein Objekt Kontext benötigt, geben Sie ihn als Konstruktorargument an.
Stephen C
Vielen Dank, aber in diesem Szenario würde es nicht funktionieren, Werte als Argumente zu übergeben. Ich benötige verschiedene Variablen, die persistent bleiben und für mehrere Funktionen zugänglich sind.
Jonathon Oates
1

Andere Antworten erklären die meisten mit anonymer Funktion, wie in diesem Artikel erwähnt,

Anonyme Funktionen sind schwer zu debuggen, zu warten, zu testen oder wiederzuverwenden.

Hier sind Beispiele mit normaler Funktion. Es ist leichter zu lesen und zu verstehen.

/* global variable example */

    var a= 3, b= 6;
    
    function fwithglobal(){
    console.log(a, b); // 3 6 expected
    }
    
    fwithglobal(); // first call
    
    function swithglobal(){
    var a=9;
    console.log(a, b); // not 3 6 but 9 6
    }
    
    swithglobal(); // second call
    

/* global variable alternative(function parameter) */

    function altern(){
    var a= 3, b= 6; // var keyword needed
      f_func(a,b);
      s_func(a,b);
    }
    
    function f_func(n, m){
    console.log(n, m); // 3 6 expected
    }
    
    function s_func(n, m){
    var a=9;
    console.log(n, m); // 3 6 expected
    }
    
    altern(); // only once

Guspan Tanadi
quelle
1

var ASHIVA_HandsOffNHS = (function() {
    
    // VARIABLES

    var my_var = 10;


    // PRIVATE FUNCTIONS
    
    function bar() {
        window.alert(my_var + 5);
    }


   // PUBLIC OBJECT

    myObject = {};
    
    myObject['a_func'] = function() {
            my_var += 10;
            window.alert(my_var);
        };
        
    myObject['b_func'] = function() {
            my_var = 0;
            window.alert(my_var);
        };

    return myObject;

})();

ASHIVA_HandsOffNHS.a_func();
ASHIVA_HandsOffNHS.b_func();
ASHIVA_HandsOffNHS.a_func();

Rounin
quelle
0

Globale Variablen sind schlecht ... wenn sie nicht verwaltet werden!

Das potenzielle Risiko globaler Variablen ist so hoch wie die Freude und Produktivitätssteigerung, häufig verwendete Objekte einsatzbereit zu haben.

Ich glaube nicht, dass man eine einzige Alternative suchen sollte. Stattdessen befürworte ich ein Objekt, das für die Verwaltung dieser Globals verantwortlich ist, und überarbeite sie, wenn die Codebasis / Komponente reift

Eine Sache, die in den aktuellen Antworten nicht erwähnt wird und die ich für kritisch halte, ist das Verständnis von DI- und IoC-Containern. Diese befassen sich mit vielen der Probleme, die Menschen mit globalen Variablen zu lösen versuchen, decken jedoch verwandte Probleme ab, die einfache Globale nicht können, wie z. B. Objektlebenszyklen.

SystematicFrank
quelle