`new function ()` mit Kleinbuchstaben "f" in JavaScript

106

Mein Kollege hat "new function ()" mit einem Kleinbuchstaben "f" verwendet, um neue Objekte in JavaScript zu definieren. Es scheint in allen gängigen Browsern gut zu funktionieren und es scheint auch ziemlich effektiv zu sein, private Variablen zu verbergen. Hier ist ein Beispiel:

    var someObj = new function () {
        var inner = 'some value';
        this.foo = 'blah';

        this.get_inner = function () {
            return inner;
        };

        this.set_inner = function (s) {
            inner = s;
        };
    };

Sobald "this" verwendet wird, wird es ein öffentliches Eigentum von someObj. SomeObj.foo, someObj.get_inner () und someObj.set_inner () sind also alle öffentlich verfügbar. Darüber hinaus sind set_inner () und get_inner () privilegierte Methoden, sodass sie über Closures Zugriff auf "inner" haben.

Ich habe jedoch nirgendwo einen Hinweis auf diese Technik gesehen. Sogar Douglas Crockfords JSLint beschwert sich darüber:

  • seltsame Konstruktion. 'Neu' löschen

Wir verwenden diese Technik in der Produktion und sie scheint gut zu funktionieren, aber ich bin etwas besorgt darüber, weil sie nirgendwo dokumentiert ist. Weiß jemand, ob dies eine gültige Technik ist?

Johnny Oshika
quelle
6
Ich bevorzuge Ihr Konstrukt gegenüber dem IIFE ('Sofort aufgerufene Funktion'). 1: Sie benötigen kein explizites 'Instanz'-Objekt, genau das ist' dies 'in JavaScript. 2: Sie müssen nichts zurückgeben, was bedeutet, dass Sie sich nicht daran erinnern müssen. Sogar der Autor der akzeptierten Antwort hat vergessen, das Instanzobjekt zunächst zurückzugeben! Menschen bevorzugen normalerweise die Verwendung eines IIFE, wenn sie Neues hassen. Dies aus gutem Grund. Wenn Sie eine Funktion haben, die ein DOM-Ereignis behandelt, thisbezieht sich dies auf das Element, das das Ereignis ausgelöst hat, nicht auf Ihr Objekt, sondern auf ein Element, das Sie var instance = thisstattdessen haben könnten .
Lee Kowalkowski
1
Warum ist es für die Frage wichtig, "Kleinbuchstaben f" anzugeben?
ClearCloud8
7
Denn in Javascript gibt es auch die Funktion 'Funktion' (mit Großbuchstaben F), die sich unterscheidet: Funktion ist eine Konstruktorfunktion, die neue Funktionsobjekte erstellen kann, während Funktion ein Schlüsselwort ist.
Stijn de Witt
@Bergi Ich habe deine Links gelesen. Ich sehe keinen Grund, dieses Muster zu diskreditieren. Es ist gültig. Es ist einfach. Also, was ist falsch. JSLint beschwert sich über alles BTW :)
Stijn de Witt

Antworten:

64

Ich habe diese Technik schon einmal gesehen. Sie ist gültig. Sie verwenden einen Funktionsausdruck, als wäre es eine Konstruktorfunktion .

Aber meiner Meinung nach können Sie dasselbe mit einem automatisch aufrufenden Funktionsausdruck erreichen. Ich sehe keinen Sinn darin, den newOperator auf diese Weise zu verwenden:

var someObj = (function () {
    var instance = {},
        inner = 'some value';

    instance.foo = 'blah';

    instance.get_inner = function () {
        return inner;
    };

    instance.set_inner = function (s) {
        inner = s;
    };

    return instance;
})();

Der Zweck des newOperators besteht darin, neue Objektinstanzen zu erstellen und die [[Prototype]]interne Eigenschaft einzurichten . Sie können sehen, wie dies durch die [Construct]interne Eigenschaft erfolgt.

Der obige Code führt zu einem äquivalenten Ergebnis.

CMS
quelle
1
Die ECMAScript 262-Spezifikation in Abschnitt 13 erläutert dies etwas formeller. So etwas wie function foo () {}gibt das Ergebnis der Erstellung eines FunctionObjekts zurück [vermutlich mit neuer Funktion ()]. Es ist Syntaxzucker.
Clinton Pierce
3
Ich denke du vermisst return instance;am Ende einen. Sonst someObjwird es einfach sein undefined. :-)
Matthew Crumley
1
Darf ich vorschlagen, dass Sie, wenn Sie sich für Modularität und das Verstecken von Informationen interessieren, dies einfach aufgeben und etwas wie require.js verwenden? Du bist auf halbem Weg, warum hier aufhören? Die asynchrone Moduldefinition (die von require.js implementiert wird) unterstützt diesen Anwendungsfall und bietet Ihnen ein vollständiges Toolset für das Scoping, den Namespace und das Abhängigkeitsmanagement.
Stijn de Witt
Beachten Sie, dass die Klammern um die Funktionsdeklaration nicht erforderlich sind, da die Anweisung aufgrund des Vorhandenseins von=
Explosion Pills
@StijndeWitt auf halbem Weg bedeutet, dass Sie die doppelte Arbeit erledigen müssten, um require.js zu verwenden, aber dies könnte in einfachen Fällen alles sein, was Sie brauchen.
xr280xr
15

Ihr Code ähnelt dem weniger seltsamen Konstrukt

function Foo () {
    var inner = 'some value';
    this.foo = 'blah';

    ...
};
var someObj = new Foo;
kennytm
quelle
9
Es ist nicht nur ähnlich, es macht genau das Gleiche ... mit der einzigen Ausnahme, dass sie Foo nicht zum Erstellen eines anderen Objekts wiederverwenden können.
Kikito
3
Die OP-Version könnte über den neuen someObj.constructor wiederverwendet werden . Hier wird der Konstruktor explizit zum Namespace hinzugefügt. Der richtige Stil hängt vom Verwendungszweck der Funktion ab. Mit diesem Stil - obwohl sicherlich der Standard - kann jemand den globalen Namespace füllen, wenn er vor Foo etwas Neues vergisst .
J Bryan Price
@kikito was meinst du damit, dass Foo nicht zum Erstellen eines anderen Objekts wiederverwendet werden kann? var newObj = new Foo () sollte eine neue Instanz erstellen.
Bill Yang
1
@ BillYang Das war vor 5 Jahren. Keine Ahnung. Ich habe Javascript seitdem nicht mehr berührt.
Kikito
12

Um einige Aspekte zu klären und Douglas Crockfords JSLint dazu zu bringen, sich nicht über Ihren Code zu beschweren, hier einige Beispiele für die Instanziierung:

1. o = new Object(); // normal call of a constructor

2. o = new Object;   // accepted call of a constructor

3. var someObj = new (function () {  
    var inner = 'some value';
    this.foo = 'blah';

    this.get_inner = function () {
        return inner;
    };

    this.set_inner = function (s) {
        inner = s;
    };
})(); // normal call of a constructor

4. var someObj = new (function () {  
    var inner = 'some value';
    this.foo = 'blah';

    this.get_inner = function () {
        return inner;
    };

    this.set_inner = function (s) {
        inner = s;
    };
}); // accepted call of a constructor

In Beispiel 3. ist der Ausdruck in (...) als Wert eine Funktion / ein Konstruktor. Es sieht so aus: new (function () {...}) (). Wenn wir also Endklammern wie in Beispiel 2 weglassen, ist der Ausdruck immer noch ein gültiger Konstruktoraufruf und sieht aus wie Beispiel 4.

Douglas Crockfords JSLint "denkt", dass Sie die Funktion someObj zuweisen wollten, nicht seiner Instanz. Und schließlich ist es nur eine Warnung, kein Fehler.

DUzun
quelle