`nowa funkcja ()` z małą literą „f” w JavaScript

106

Mój kolega używa „nowej funkcji ()” z małą literą „f” do definiowania nowych obiektów w JavaScript. Wydaje się, że działa dobrze we wszystkich głównych przeglądarkach, a także wydaje się być dość skuteczny w ukrywaniu zmiennych prywatnych. Oto przykład:

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

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

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

Jak tylko "this" zostanie użyte, staje się publiczną własnością jakiegoś obiektuObj. Więc someObj.foo, someObj.get_inner () i someObj.set_inner () są dostępne publicznie. Dodatkowo set_inner () i get_inner () są metodami uprzywilejowanymi, więc mają dostęp do "inner" poprzez domknięcia.

Jednak nigdzie nie widziałem żadnego odniesienia do tej techniki. Nawet JSLint Douglasa Crockforda narzeka na to:

  • dziwna konstrukcja. Usuń „nowy”

Używamy tej techniki w produkcji i wydaje się, że działa dobrze, ale trochę mnie to niepokoi, ponieważ nie jest nigdzie udokumentowane. Czy ktoś wie, czy to poprawna technika?

Johnny Oshika
źródło
6
Wolę twoją konstrukcję od IIFE („funkcja wywoływana natychmiastowo”). 1: Nie potrzebujesz wyraźnego obiektu „instancja”, dokładnie tym jest „to” w JavaScript. 2: Nie musisz niczego zwracać, co oznacza, że ​​nie musisz o tym pamiętać. Nawet autor zaakceptowanej odpowiedzi zapomniał początkowo zwrócić obiekt instancji! Ludzie zwykle wolą używać IIFE, jeśli nienawidzą nowego i to z dobrego powodu - jeśli masz funkcję obsługującą zdarzenie DOM, thisbędzie odnosić się do elementu, który wywołał zdarzenie, a nie do obiektu, ale możesz po prostu to zrobić var instance = this.
Lee Kowalkowski
1
Dlaczego w pytaniu ważne jest określenie „małej litery f”?
ClearCloud8
7
Ponieważ w Javascript istnieje również funkcja „Function” (z wielką literą F), która jest inna: Funkcja to funkcja konstruktora, która może tworzyć nowe obiekty funkcji, podczas gdy funkcja jest słowem kluczowym.
Stijn de Witt,
@Bergi Czytałem twoje linki. Nie widzę powodu, aby zdyskredytować ten wzór. To jest ważne. To proste. Więc co się stało. JSLint narzeka na wszystko BTW :)
Stijn de Witt

Odpowiedzi:

64

Widziałem już tę technikę, jest prawidłowa, używasz wyrażenia funkcyjnego tak, jakby to była funkcja konstruktora .

Ale IMHO, możesz osiągnąć to samo za pomocą wyrażenia funkcji automatycznego wywoływania, naprawdę nie widzę sensu używania newoperatora w ten sposób:

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;
})();

Celem newoperatora jest tworzenie nowych instancji obiektów, ustawianie [[Prototype]]właściwości wewnętrznej, możesz zobaczyć, jak jest to wykonane przez [Construct]właściwość wewnętrzną.

Powyższy kod da równoważny wynik.

CMS
źródło
1
Specyfikacja ECMAScript 262 w sekcji 13 wyjaśnia to nieco bardziej formalnie. Coś podobnego function foo () {}zwraca wynik tworzenia Functionobiektu [prawdopodobnie z nową funkcją ()]. To cukier składniowy.
Clinton Pierce
3
Myślę, że brakuje ci return instance;na końcu. W przeciwnym razie someObjpo prostu będzie undefined. :-)
Matthew Crumley
1
Czy mogę zasugerować, jeśli zależy Ci na modułowości i ukrywaniu informacji, to po prostu zrezygnuj z tego i zacznij używać czegoś takiego jak require.js? Jesteś w połowie drogi, po co tu się zatrzymywać? Asynchroniczna definicja modułu (która jest tym, co implementuje require.js) obsługuje ten przypadek użycia i zapewnia cały zestaw narzędzi do obsługi zakresu, przestrzeni nazw i zarządzania zależnościami.
Stijn de Witt,
Zwróć uwagę, że nawiasy otaczające deklarację funkcji są niepotrzebne, ponieważ instrukcja jest już wyrażeniem z powodu obecności=
Explosion Pills
@StijndeWitt w połowie drogi oznacza, że ​​będziesz musiał wykonać dwa razy więcej pracy, aby użyć require.js, ale może to być wszystko, czego potrzebujesz w prostych przypadkach.
xr280xr
15

Twój kod jest podobny do mniej dziwnej konstrukcji

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

    ...
};
var someObj = new Foo;
kennytm
źródło
9
Nie jest po prostu podobny, robi dokładnie to samo ... z jedynym wyjątkiem, że nie będą mogli ponownie użyć Foo do stworzenia innego obiektu.
kikito
3
Wersja OP może być ponownie wykorzystana za pośrednictwem nowego someObj.constructor . Tutaj konstruktor jest jawnie dodawany do przestrzeni nazw; właściwy styl zależy od zamierzonego celu funkcji. Ponadto, ten styl - choć z pewnością standardowy - pozwala komuś zapełnić globalną przestrzeń nazw, jeśli zapomni o nowym przed Foo .
J Bryan Price
@kikito, co masz na myśli, mówiąc, że to nie pozwala na ponowne użycie Foo do stworzenia kolejnego obiektu? var newObj = new Foo () powinno tworzyć nową instancję.
Bill Yang
1
@BillYang To było 5 lat temu. Brak pomysłu. Od tamtej pory nie tknąłem javascript.
kikito
12

Aby wyjaśnić niektóre aspekty i sprawić, by JSLint Douglasa Crockforda nie narzekał na Twój kod, oto kilka przykładów instancji:

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

W przykładzie 3. wyrażenie w (...) jako wartość jest funkcją / konstruktorem. Wygląda to tak: new (function () {...}) (). Więc jeśli pominiemy nawiasy końcowe, jak w przykładzie 2, wyrażenie jest nadal prawidłowym wywołaniem konstruktora i wygląda jak przykład 4.

JSLint Douglasa Crockforda „myśli”, że chcesz przypisać funkcję do jakiegoś obiektuObj, a nie do jego instancji. W końcu to tylko ostrzeżenie, a nie błąd.

DUzun
źródło