3 Stimmen

Was ist die "x = x || {}" Technik in JavaScript - und wie beeinflusst sie dieses IIFE?

Zunächst ein Pseudocode-Beispiel:

;(function(foo){

    foo.init = function(baz) { ... }

    foo.other = function() { ... }

    return foo;

}(window.FOO = window.FOO || {}));

So aufgerufen:

FOO.init();

Meine Frage:

  • Wie lautet der technische Name / die Beschreibung von: window.FOO = window.FOO || {}?

Ich verstehe, was der Code macht... Siehe unten für meinen Grund(e) für die Frage.


Grund für die Frage:

Ich rufe das übergebene globale Objekt so auf:

;(function(foo){
    ... foo vs. FOO, jemand anderes möglicherweise verwirrt? ...
}(window.FOO = window.FOO || {}));

... aber ich mag es einfach nicht, dieses kleine "foo" zu nennen, wenn das globale Objekt großgeschrieben FOO heißt... Es scheint einfach verwirrend.

Wenn ich den technischen Namen dieser Technik kennen würde, könnte ich sagen:

;(function(technischername){
    ... mache etwas mit technischername, nicht zu verwechseln mit FOO ...
}(window.FOO = window.FOO || {}));

Ich habe vor kurzem ein (super) Beispiel gesehen, bei dem sie es "exports" genannt haben:

;(function(exports){
    ...
}(window.Lib = window.Lib || {}));

Ich versuche einfach nur, meine Kodierungsstandards zu standardisieren... Ich würde gerne lernen, was die Profis tun und wie sie denken (deswegen frage ich hier)!

4voto

zzzzBov Punkte 166065

Muster

(function (foo) {
    ...Code...
    foo.bar = baz;
    ...More Code...
}(window.FOO = window.FOO || {});

Es gibt keinen formalen Namen für das von Ihnen beschriebene Muster, da es sich um drei separate Muster handelt, die kombiniert sind. Jedes Muster hat mehrere Namen, aber in diesem Beitrag verwende ich folgende Terminologie:

  • Verschluss
  • Alias
  • Namespace-Erweiterung

Verschluss

Die Grundlage des gesamten Musters ist der Verschluss. Es handelt sich einfach um eine Funktion, die verwendet wird, um Variablen und Funktionen zu schützen, so dass sie nicht den globalen Namensraum verschmutzen:

Kein Verschluss

// diese deklarieren window.foo und window.bar jeweils
// als solche verschmutzen sie den globalen Namensraum
var foo;
function bar() {}

Verschluss, in diesem Fall ein <a href="http://benalman.com/news/2010/11/immediately-invoked-function-expression/">Sofort eingefügte Funktionsausdruck (IIFE)</a>

(function () {
    // diese deklarieren foo und bar innerhalb der Funktion
    // aber sie sind außerhalb der Funktion nicht zugänglich
    var foo;
    function bar() {}
}());

Der Vorteil, Variablen innerhalb eines Verschlusses zu halten, besteht darin, dass Sie sich keine Sorgen darüber machen müssen, dass jemand die von Ihnen verwendeten Variablen überschreibt. Dies ist besonders wichtig für temporäre Variablen wie i oder j, die häufig verwendet werden.

Alias

Der zweite wichtige Teil dieses Musters ist die Aliasbildung. Durch die Aliasbildung kann eine Variable innerhalb eines Verschlusses definiert und verwendet werden, ohne sich darum kümmern zu müssen, in welchem globalen Namensraum sie sich befindet.

Ohne Aliasbildung

(function () {
    ...
    foo = window.SomeFunction(bar, baz);
    ...
}());

Mit Aliasbildung

(function (sf) { //lokaler Name
    ...
    foo = sf(bar, baz);
    ...
}(window.SomeFunction)); //globaler Namensraum

Dies ist besonders wichtig, denn dadurch kann der globale Namensraum in einer großen JavaScript-Datei geändert werden, indem der Name an einer einzigen Stelle geändert wird. Dies ist Eine gute Sache™. Darüber hinaus können Minifizierer den internen Alias auf einen einzelnen Buchstabenvariablennamen wie a verkürzen, was bei der Minifizierung zu erheblichen Einsparungen an Bytes führt.

Namespace-Erweiterung

Das Muster der Namespace-Erweiterung basiert auf dem Zusammenführenverhalten des Oder-Operators (||). In vielen Sprachen geben && und || entweder true oder false zurück, aber in JavaScript gibt && den ersten falsey-Wert zurück (false, 0, '', null, undefined), und || gibt den ersten truthy-Wert zurück (alles, was nicht falsey ist). Für beide Operatoren wird bei einer nicht gefundenen Art der letzte Argument zurückgegeben. Dies macht den ||-Operator zu einer bequemen Möglichkeit, einen neuen Namespace nur dann zu definieren, wenn er noch nicht vorhanden ist.

Ohne Namespace-Erweiterung

if (typeof window.Foo === 'undefined') {
    window.foo = {};
}

Mit Namespace-Erweiterung

window.foo = window.foo || {};

Dies ist nützlich, da es erlaubt, einen Namespace mit zusätzlichen Eigenschaften und Methoden zu erweitern, ohne sich Gedanken darüber machen zu müssen, in welcher Reihenfolge die Eigenschaften und Methoden definiert wurden.

In diesem ersten Beispiel müsste FileA vor FileB ausgeführt werden:

FileA.js

window.foo = {};
window.foo.bar = 'baz';

FileB.js

window.foo.fizz = 'buzz';

In diesem zweiten Beispiel könnten File1 und File2 in beliebiger Reihenfolge ausgeführt werden:

File1.js

window.foo = window.foo || {};
window.foo.bar = 'baz';

File2.js

window.foo = window.foo || {};
window.foo.fizz = 'buzz';

Alles zusammen

Die Verwendung jedes Musters zusammen schafft ein sehr leistungsfähiges modulares Skript:

// benutze foo intern, damit du dir keine Sorgen darüber machen musst,
// wie der globale Namensraum heißt
(function (foo) {
    // deklariere Variablen intern, die du lokal im Skript behalten möchtest
    var i,
        len,
        intern,
        qux;
    // deklariere Funktionen/Eigenschaften des Alias, wenn du sie freigeben möchtest
    foo.bar = function () {...};
// erweitere den globalen Namensraum, damit bestehende Erweiterungen erhalten bleiben
}(window.FOO = window.FOO || {}));

2voto

pete Punkte 22815

Ich habe es immer als "Null Coalescing" verstanden.

Was deine IIFE betrifft, übergibst du window.FOO, falls es bereits instanziiert ist, oder ein leeres Objekt, wenn nicht.

Man könnte es auch so lesen:

window.FOO = window.FOO || {};
;(function(foo){

    foo.init = function(baz) { ... }

    foo.other = function() { ... }

    return foo;

}(window.FOO));

Persönlich bevorzuge ich ein anderes Muster:

var FOO;
if (!FOO) {
    FOO = {};
}
(function () {
    "use strict";
    FOO.prop1 = 'bar';
    FOO.bar = function (z) {
        return z + 1;
    };
}());

Ich finde es weniger verwirrend und hilft mir, eine saubere Namensgebung sicherzustellen.

2voto

Renato Zannon Punkte 26657

Um petes Antwort zu ergänzen, eine alternative Form:

;(function() {
  var Foo = window.Foo = window.Foo || {};

  Foo.foo  = 'bar';
  Foo.baz  = function() {
    return "Hallo Welt!";
  };
})();

Ich benutze normalerweise das lokale var, um dem Minifier die Möglichkeit zu geben, ein paar Bytes zu sparen. Dies hat eine größere Wirkung, wenn Sie auf mehreren Ebenen in Ihrem Namensraum arbeiten, wie bei var MyView = window.MyApp.Views.MyView.

1voto

slebetman Punkte 100693

Wie andere bereits festgestellt haben, ist Ihre erste Frage vollständig separat und unabhängig von Ihrer zweiten Frage. Sie haben nichts miteinander zu tun, außer der Tatsache, dass Sie sich entschieden haben, sie in einer einzigen Aussage zu kombinieren, anstatt es in zwei Aussagen aufzuteilen.

Für Ihre erste Frage nennt Douglas Crockford es Standardzuweisung. Das bedeutet, dass die Variable unverändert bleibt, wenn sie bereits existiert, andernfalls wird sie mit dem angegebenen Standardwert initialisiert. Wenn Sie etwas sehen, das aussieht wie:

foo = foo || {};

Sollten Sie es in Gedanken so lesen:

foo wird auf `{} `gesetzt

Technisch gesehen heißt es wirklich "weise {} foo zu, wenn foo falsy ist", aber wir gehen davon aus, dass alles Falsy, wie Null, oder Undefiniert, ein ungültiger Wert für foo ist, wenn wir es verwenden. Außerdem impliziert das Wort default bereits "wenn foo nicht definiert ist".

Aber, trotzdem und wenn man ihre zweite Frage betrachtet, ist offensichtlich, dass Standard nicht wirklich das richtige Wort für den an Ihre IIFE übergebenen Parameter ist. Was dieser Parameter tut, ist einfach Methoden und Attribute an das übergebene Objekt anhängen. In diesem Fall ist exports angemessen, als "dies sind die öffentlich exportierten Elemente des Objekts". Ich denke, attach ist auch ein passender Name, als "hänge diese Dinge an das Objekt". Meine persönliche Präferenz ist es einfach, es als obj zu benennen, als Objekt, das ist die Art von Sache, die Sie erwarten sollten.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X