Anonimowe zamknięcia z autoreferencją: czy JavaScript jest niekompletny?


18

Czy fakt, że anonimowe zamknięcia funkcji odwołujących się do siebie są tak popularne w JavaScript, sugeruje, że JavaScript jest niekompletną specyfikacją? Widzimy tak wiele z tego:

(function () { /* do cool stuff */ })();

i przypuszczam, że wszystko jest kwestią gustu, ale czy to nie wygląda jak kludge, kiedy wszystko, czego chcesz, to prywatna przestrzeń nazw? Czy JavaScript nie może zaimplementować pakietów i odpowiednich klas?

Porównaj z ActionScript 3, również opartym na ECMAScript, gdzie masz

package com.tomauger {
  import bar;
  class Foo {
     public function Foo(){
       // etc...
     }

     public function show(){
       // show stuff
     }

     public function hide(){
       // hide stuff
     }
     // etc...
  }
}

W przeciwieństwie do zwojów, które wykonujemy w JavaScript (to z dokumentacji autorskiej wtyczki jQuery ):

(function( $ ){

  var methods = {
    init : function( options ) { // THIS },
    show : function( ) { // IS   },
    hide : function( ) { // GOOD },
    update : function( content ) { // !!! }
  };

  $.fn.tooltip = function( method ) {

    // Method calling logic
    if ( methods[method] ) {
      return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }    

  };

})( jQuery );

Rozumiem, że to pytanie z łatwością może przerodzić się w rant na temat preferencji i stylów programowania, ale tak naprawdę jestem bardzo ciekawy, jak przyprawiliście się o tym doświadczeni programiści i czy wydaje się to naturalne, jak uczenie się różnych osobliwości nowego języka lub kludgy , jak obejście niektórych podstawowych składników języka programowania, które po prostu nie zostały zaimplementowane?


22
„Czy JavaScript nie może zaimplementować ... odpowiednich klas?” Nie. Ma już odpowiednie prototypy. Prototypy nie są gorsze od klas. Oni są różni. Ludzie próbowali dodawać klasy do JavaScript w różnych momentach i raczej nie powiodło się.
Rein Henrichs

5
@Rein: A jednak jakoś udało się to ActionScript ...
Mason Wheeler

8
@Tom nie ma „wbudowanych klas”. Nie ma czegoś takiego jak klasa w języku prototypowym . Wciąż łączysz te dwa paradygmaty.
Rein Henrichs

1
Osobiście uważam, że funkcja anonimowego języka funkcji jest bardziej elastyczna niż zajęcia. Lubię jednak programowanie funkcjonalne, w którym ten idiom jest powszechny.
dietbuddha

1
Dla innych tutaj: brianodell.net/?page_id=516 jest niesamowitym podkładem w JavaScript jako języku prototypowym.
Tom Auger

Odpowiedzi:


9

Przypuszczam, że wszystko jest kwestią gustu, ale czy to nie wygląda jak kludge, kiedy wszystko, czego chcesz, to prywatna przestrzeń nazw? Czy JavaScript nie mógł zaimplementować pakietów i odpowiednich klas?

Większość komentarzy jest sprzecznych z mitem, że „prototypy są klasami biednych ludzi”, więc powtórzę, że OO oparte na prototypach nie jest gorsze od OO opartego na klasach.

Drugi punkt to „kludge, gdy wszystko, czego chcesz, to prywatna przestrzeń nazw”. Możesz być zaskoczony, wiedząc, że Schemat używa dokładnie tej samej kludge do definiowania zakresów. To nie powstrzymało od stania się archetypowym przykładem dobrze wykonanego zakresu leksykalnego.

Oczywiście w schemacie „kludge” jest ukryty za makrami ...


1
Nie przedstawiasz żadnych dowodów na poparcie swojego twierdzenia, że ​​Schemat jest podstawowym przykładem dobrze wykonanego zakresu leksykalnego lub że ma to coś wspólnego ze sposobem, w jaki Scheme użył funkcji do zdefiniowania zakresów.
DeadMG

Nie mogę mówić o tym, że Scheme jest przykładem, ale jednym z przykładów współautora JS Brendana Eicha omawiającego Scheme odgrywającego rolę w projekcie JS tutaj: readwrite.com/2011/07/22/javascript-was-no-accident
Erik Reppen

7

Po pierwsze, kilka rzeczy:

  1. Innym sposobem patrzenia na JavaScript jest 1 milion i 1 rzeczy, które możesz zrobić z funkcją jako konstrukcją. Wszystko tam jest, jeśli jej szukasz. Po prostu nigdy nie jest daleko od funkcji.

  2. Ta kwestia tworzenia wtyczek jQuery jest okropna. Nie mam pojęcia, dlaczego oni to popierają. Rozszerzenia $ powinny być rzeczami ogólnego użytku, które $ już ma całkiem dobrze pokryte, a nie metodami budowania mnie jako kompletnego widgetu. To narzędzie normalizacyjne DOM-API. Jego użycie najlepiej jest zakopać wewnątrz własnych obiektów. Nie widzę potrzeby używania go jako pełnego repozytorium biblioteki interfejsu użytkownika.

Pakiety w sieci po stronie klienta są bezcelowe

To, czego osobiście nie lubię w pakietach po stronie klienta, to to, że udajemy, że robimy coś, czym tak naprawdę nie jesteśmy. W post-webformach internetowych i gobach przerażających rzeczy, których nigdy nie wymanewrowałeś z naszych przyjaciół Java, wolałbym myśleć o kawałku HTML z połączonymi zasobami jako tym, czym naprawdę jest i nie próbujcie uspokoić programistów aplikacji odpornych na naukę nowych systemów operacyjnych, udając, że to coś innego. W JS w sieci po stronie klienta nic nie zostaje „zaimportowane”, z wyjątkiem robienia czegoś okropnego z Ajaxem, który działa w nieświadomości buforowania przeglądarki, co tak wielu próbowało zrobić. Dla przeglądarki liczy się tylko to, że została ona załadowana i zinterpretowana lub nie. Nie mamy więcej kodu ukrytego na kliencie dostępnym do użycia „na wszelki wypadek” z dobrych powodów. # 1 to, że właśnie opisałem zależności wtyczek i przeglądarek dla aplikacji internetowych, ponieważ zjawisko to na ogół nie działało zbyt dobrze. Chcemy teraz sieci. Nie po tym, jak Adobe lub Sun dokonają aktualizacji po raz trzeci w tym tygodniu.

Język ma to, czego potrzebuje do struktury

Obiekty JS są wysoce zmienne. Możemy mieć rozgałęzione drzewa przestrzeni nazw w każdym stopniu, w którym uznamy to za przydatne i jest to bardzo łatwe. Ale tak, w przypadku wszystkiego, co można ponownie wykorzystać, musisz przykleić katalog główny dowolnej biblioteki do globalnej przestrzeni. W każdym razie wszystkie zależności są powiązane i ładowane jednocześnie, więc po co robić cokolwiek innego? Celem unikania globalnej przestrzeni nazw nie jest to, że wszystko jest złe. Jest zbyt wiele rzeczy, które są złe, ponieważ ryzykujesz kolizją przestrzeni nazw lub przypadkowym zastąpieniem podstawowych funkcji języka.

To, że jest popularny, nie oznacza, że ​​robimy to dobrze

Teraz, gdy zobaczysz to w aplikacji internetowej po stronie klienta:

(function(){
//lots of functions defined and fired and statement code here
})()

Problemem nie jest to, że brakuje nam narzędzi do strukturyzacji aplikacji, problem polega na tym, że ludzie nie cenią struktury. W przypadku 2-3 stron jednorazowych tymczasowych stron internetowych z agencji projektowej naprawdę nie mam z tym problemu. Kiedy robi się brzydko, musisz zbudować coś łatwego w utrzymaniu, czytelnym i łatwym do modyfikacji.

Ale kiedy dotrzesz do miejsca, w którym nadszedł czas, aby po prostu zaimplementować wszystkie przedmioty wielokrotnego użytku i fabryki, a może jeden lub dwa nowe tymczasowe pojazdy mogą wkraść się w ten proces, to wygoda.

Ale są implementacje JS z pakietami / modułami

Należy pamiętać, że w Node.js, gdzie takie rzeczy mają o wiele większy sens, mają moduły. JS, zakładając, że możemy uniknąć uber-config-hell, który plagi innych języków, jest jedyną rzeczą w równaniu, a każdy wykonywany plik ma swój własny izolowany zakres. Ale na stronie internetowej samo połączenie pliku js jest instrukcją importu. Wykonanie większego importu w locie to tylko strata czasu i zasobów, ponieważ uzyskanie zasobów wymaga dużo więcej wysiłku niż zwykłe dodawanie linków do plików, ponieważ potrzebujesz ich, wiedząc, że zostaną one zapisane w pamięci podręcznej w przeglądarce, jeśli kolejna strona będzie ich ponownie potrzebować. Próbuje więc podzielić przestrzeń globalną, robiąc cokolwiek innego niż tworzenie fabryk obiektów adapterów, takich jak jQuery lub bardziej tradycyjne obiekty, które obejmują duży podzbiór zadań w danej domenie, zajmując jedno miejsce w globalnym. Tam'http://wiki.ecmascript.org/doku.php?id=harmony:modules

Więc nie, nie ma nic złego w automatycznych wywoływaczach używanych do unikania globalnego zanieczyszczenia przestrzeni nazw, gdy istnieje dobry powód, aby używać takich rzeczy (częściej niż nie ma). I mamy trwałe właściwości ekwiwalentu prywatnego w naszych obiektach (wystarczy zdefiniować zmienną w konstruktorze i nie ujawniać jej jako właściwości).

Jednak fakt, że MOŻEMY robić takie rzeczy, jest niesamowity. Częste użycie jest oznaką, że programiści JS wciąż mogą dojrzewać, ale nie jest to luka w języku dla każdego, kto nie próbuje narzucić paradygmatu na stronę po stronie klienta, co po prostu nie ma sensu.


Wyborcy, czy mógłbyś wyjaśnić dlaczego? Kiedy ktoś tak dużo pisze, zasługuje na wyjaśnienie!
Songo,

+1 dobra odpowiedź, nie wiem, dlaczego wcześniej głosował w dół.
pllee

Niesamowite napisanie i świetna perspektywa. Podoba mi się również „tylko dlatego, że to trop nie oznacza, że ​​ma rację”. Myślę, że moim problemem jest to, że czuję się bardziej komfortowo z bardziej rygorystycznymi językami, co (IMO) pomaga na wiele sposobów poprawić wydajność rozwoju. JavaScript wydaje się naprawdę nieprzyzwoity, nie ma wielu wbudowanych czeków i równowagi: możesz robić, co chcesz, stąd krajobraz jest bałaganem idiomów i praktyk. Trudno jest ustalić „właściwy” sposób podejścia do struktury kodowania. Chociaż zgadzam się, że w przypadku szybkich jednorazowych zadań nie jest to duży problem.
Tom Auger

1
IMO, jest wiele rzeczy, które możesz zrobić z dużą ilością sznurka, poza powieszeniem się i uczysz się szybkiego pisania solidnego kodu z okazjonalnych samopodwieszeń, które zdarzają się rzadziej, gdy rozwijasz lepsze nawyki, ale nie będę udawać, że to dla każdy lub idealny kandydat do każdej pracy. Podejrzewam, że im więcej się o tym dowiesz, tym bardziej będziesz tolerować. Czuję, że brakuje mi połowy mojego mózgu, gdy próbuję robić rzeczy w językach bez pierwszorzędnych funkcji lub obiektów tak elastycznych / zmiennych jak JS.
Erik Reppen

4

Inną rzeczą, której brakuje, jest to, że javscript musi być kompatybilny wstecz. Jeśli spróbujesz wprowadzić składnię pakietu, może to naprawdę popsuć sieć w szalony sposób. To by było złe! Doug Crockford mówił o tym w różnych punktach i dlaczego próby dodania go nie powiodły się.


Trafne spostrzeżenie. A jednak ActionScript to zarządzał, po prostu wydając nową wersję. Podczas definiowania tagu skryptu zawsze można było określić wersję JavaScript, więc „łamanie” istniejących witryn powinno być problemem.
Tom Auger

1
W praktyce większość tagów skryptów w sieci nie ma numeru wersji. Szczerze mówiąc, nie jestem pewien wszystkich problemów w tej sprawie, ale wiem, że ludzie, którzy myślą o tym, zdecydowali, że nie jest to wykonalne.
Zachary K

1
@Tom: Adobe ma również pełną kontrolę nad platformą Flash. Żaden podmiot nie ma pełnej kontroli nad wszystkimi platformami JS. Również proste wybranie numeru wersji skryptu JS w przeglądarce oznacza, że ​​albo nie obsługujesz starszych przeglądarek, albo musisz napisać dwa skrypty. To jest problem.
Jeremy Heiler

2

Tak, to kludge.

Wiele osób mówi, że „prototypy nie są gorsze od klas”. Nie zgadzam się, ale to kwestia preferencji. Ale to nawet nie jest prawdziwy problem z JavaScript - problem polega na tym, że został pierwotnie zaprojektowany jako szybki i brudny język skryptowy do tworzenia animowanych przycisków. W połowie lat 90. nikt nigdy nie myślał, że JavaScript zostanie poproszony o zrobienie szalonych rzeczy, które robi teraz.


6
Nie zgadzam się, JavaScript jest naprawdę niesamowity. To, że zostało połączone w całość z niedokładnie określonym i wzajemnie niekompatybilnym DOMem, właśnie tam zaczęły się wszystkie problemy.
Dean Harding

2
Co to ma wspólnego z prototypami?
Erik Reppen

2

Anonimowe funkcje wywołujące są bardziej podobne do modułów niż klas. To denerwujące, że domyślnym skryptem javascript jest uruchamianie w zakresie globalnym. Komitet pracujący nad JS.next poważnie rozważa dodanie modułów, aby nie upuścić zmiennych lokalnych w zasięgu globalnym. Na szczęście funkcje Javascript mają tak dogodną semantykę, że możemy stosunkowo łatwo używać funkcji anonimowej jako zakresu prywatnego.

Nie rozumiem, w jaki sposób klasy naprawdę w ogóle biorą udział w dyskusji, z wyjątkiem tego, że są konstrukcją określającą zakres najwyższego poziomu w wielu językach. Lepiej byłoby mieć lepszą konstrukcję moduł / pakiet / proszę-daj-mi-lokalny-zasięg-tak-i-nie-zostawiaj-moje-zmienne-w-globalnym środowisku.


1

Możesz rzucić okiem na ExtJS 3 i 4, gdzie udało im się całkiem dobrze zaimplementować przestrzenie nazw.

- dodane po -1

Chodzi mi o to, że można ukryć wszystkie te „zwoje” i nadal mieć całkiem przyjazny kod:

Ext.ns('com.tomauger');
Ext.Loader.load('bar.js'); //unfortunately filname needs to be used
MyNameSpace.Foo = {
   //...
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.