Obecnie w ES5 wielu z nas używa następującego wzorca w ramach do tworzenia klas i zmiennych klas, co jest wygodne:
// ES 5
FrameWork.Class({
variable: 'string',
variable2: true,
init: function(){
},
addItem: function(){
}
});
W ES6 możesz tworzyć klasy natywnie, ale nie ma opcji, aby mieć zmienne klas:
// ES6
class MyClass {
const MY_CONST = 'string'; // <-- this is not possible in ES6
constructor(){
this.MY_CONST;
}
}
Niestety powyższe nie zadziała, ponieważ klasy mogą zawierać tylko metody.
Rozumiem, że mogę this.myVar = true
się constructor
... ale ja nie chcę „śmieci” mój konstruktor, zwłaszcza gdy mam 20-30 + params dla większej klasy.
Myślałem o wielu sposobach rozwiązania tego problemu, ale nie znalazłem jeszcze żadnych dobrych. (Na przykład: utwórz ClassConfig
moduł obsługi i przekaż parameter
obiekt, który jest zadeklarowany oddzielnie od klasy. Następnie moduł obsługi dołączy do klasy. Myślałem WeakMaps
też o integracji).
Jakie pomysły musiałbyś poradzić sobie w tej sytuacji?
źródło
this.member = member
u konstruktora z 20-30 parametrami?public variable2 = true
w klasie? To by to zdefiniowało na prototypie.Odpowiedzi:
Aktualizacja 2018:
Jest teraz propozycja etapu 3 - nie mogę się doczekać, aby ta odpowiedź stała się nieaktualna za kilka miesięcy.
W międzyczasie każdy, kto używa TypeScript lub babel, może użyć składni:
Wewnątrz deklaracji klasy / treści wyrażenia zdefiniuje zmienną. Mam nadzieję, że za kilka miesięcy / tygodni będę mógł opublikować aktualizację.
Aktualizacja: Chrome 74 jest teraz dostarczany z działającą składnią.
Notatki na stronie wiki ES dotyczące propozycji w ES6 ( maksymalnie minimalne klasy ) Uwaga:
Oznacza to, że to , o co prosiłeś, zostało wzięte pod uwagę i zostało wyraźnie odrzucone.
ale dlaczego?
Dobre pytanie. Dobrzy ludzie z TC39 chcą, aby deklaracje klas deklarowały i definiowały możliwości klasy. Nie jego członkowie. Deklaracja klasy ES6 określa umowę dla użytkownika.
Pamiętaj, definicja klasy definiuje metody prototypowe - definiowanie zmiennych w prototypie na ogół nie jest czymś, co robisz. Możesz oczywiście użyć:
W konstruktorze, jak sugerowałeś. Zobacz także podsumowanie konsensusu .
ES7 i nowsze
Trwają prace nad nową propozycją dla ES7, która umożliwia bardziej zwięzłe zmienne instancji poprzez deklaracje klas i wyrażenia - https://esdiscuss.org/topic/es7-property-initializers
źródło
static
nieruchomościachstatic
działa tylko dla metod.Wystarczy dodać do odpowiedzi Benjamina - zmienne klasy są możliwe, ale nie
prototype
użyłbyś ich do ustawienia.Dla zmiennej prawdziwej klasy chciałbyś zrobić coś takiego:
Z metody klasy można uzyskać dostęp do tej zmiennej jako
this.constructor.foo
(lubMyClass.foo
).Te właściwości klasy zwykle nie byłyby dostępne z poziomu instancji klasy. tzn.
MyClass.foo
daje,'bar'
alenew MyClass().foo
jestundefined
Jeśli chcesz mieć również dostęp do zmiennej klasy z instancji, musisz dodatkowo zdefiniować moduł pobierający:
Testowałem to tylko z Traceur, ale wierzę, że będzie działać tak samo w standardowej implementacji.
JavaScript tak naprawdę nie ma klas . Nawet w ES6 patrzymy na język oparty na obiektach lub prototypach, a nie język oparty na klasach. W dowolny
function X () {}
,X.prototype.constructor
wskazuje z powrotemX
. Gdynew
operator jest włączonyX
, tworzony jest nowy obiekt dziedziczącyX.prototype
. Wszystkie niezdefiniowane właściwości w tym nowym obiekcie (w tymconstructor
) są wyszukiwane stamtąd. Możemy myśleć o tym jak o generowaniu właściwości obiektu i klasy.źródło
Babel obsługuje zmienne klas w ESNext, sprawdź ten przykład :
źródło
W twoim przykładzie:
Ponieważ MY_CONST jest prymitywny https://developer.mozilla.org/en-US/docs/Glossary/Primitive możemy po prostu zrobić:
Ale jeśli
MY_CONST
jest typem odniesienia, na przykładstatic get MY_CONST() {return ['string'];}
wyjściem alertu jest ciąg, fałsz . W takim przypadkudelete
operator może wykonać lewę:I wreszcie dla zmiennej klasy nie
const
:źródło
delete
operatora, jeśli jest sam ze względów wydajnościowych. To, czego tak naprawdę chcesz tutajObject.defineProperty
.Ponieważ twój problem ma głównie charakter stylistyczny (nie chcesz wypełniać konstruktora wiązką deklaracji), możesz go rozwiązać również stylistycznie.
Z mojego punktu widzenia w wielu językach opartych na klasach konstruktor może być funkcją nazwaną po samej nazwie klasy. Stylistycznie możemy to wykorzystać, aby stworzyć klasę ES6, która stylistycznie ma sens, ale nie grupuje typowych działań zachodzących w konstruktorze ze wszystkimi deklaracjami własności, które wykonujemy. Po prostu używamy rzeczywistego konstruktora JS jako „obszaru deklaracji”, a następnie tworzymy klasę o nazwie funkcja, którą inaczej traktujemy jako obszar „innych rzeczy konstruktora”, nazywając ją na końcu prawdziwego konstruktora.
Oba zostaną wywołane w trakcie tworzenia nowej instancji.
Trochę tak, jakbyś miał 2 konstruktory, w których oddzielasz deklaracje i inne działania konstruktora, które chcesz podjąć, i stylistycznie sprawia, że nie jest zbyt trudno zrozumieć, że to się dzieje.
Uważam, że jest to dobry styl do użycia w przypadku wielu deklaracji i / lub wielu działań, które muszą się zdarzyć przy tworzeniu instancji i chcą oddzielić te dwie idee od siebie.
UWAGA : Bardzo celowo nie używam typowych idiomatycznych pomysłów „inicjalizacji” (jak metoda
init()
lubinitialize()
), ponieważ są one często używane inaczej. Istnieje pewna domniemana różnica między ideą konstruowania i inicjowania. Pracując z konstruktorami ludzie wiedzą, że są wywoływani automatycznie w ramach tworzenia instancji. Widzącinit
metodę, którą wiele osób zakłada bez drugiego spojrzenia, że muszą robić coś podobnegovar mc = MyClass(); mc.init();
, ponieważ zazwyczaj tak się inicjuje. Nie próbuję dodawać procesu inicjalizacji dla użytkownika klasy, próbuję dodać do procesu budowy samej klasy.Chociaż niektórzy ludzie mogą przez chwilę zrobić podwójne ujęcie, to w rzeczywistości o to chodzi: komunikuje im, że zamiar jest częścią konstrukcji, nawet jeśli sprawia to, że robią trochę podwójnego ujęcia i iść ”to nie jest jak działają konstruktory ES6 ”i poświęć chwilę, by spojrzeć na rzeczywistego konstruktora, aby powiedzieć„ och, nazywają to na dole, rozumiem ”, to o wiele lepsze niż NIE komunikowanie tego zamiaru (lub niepoprawne komunikowanie go) i prawdopodobnie uzyskanie dużej ilości ludzie używają go źle, próbując zainicjować go z zewnątrz i śmieci. Jest to bardzo celowe dla wzoru, który sugeruję.
Dla tych, którzy nie chcą podążać za tym wzorem, może również działać dokładnie odwrotnie. Na początku wyeksportuj deklaracje do innej funkcji. Może nazwać to „właściwości” lub „publicProperties” lub coś w tym rodzaju. Następnie włóż resztę rzeczy do normalnego konstruktora.
Zauważ, że ta druga metoda może wyglądać na czystszą, ale ma również nieodłączny problem polegający na tym, że
properties
zostaje zastąpiona, gdy jedna klasa za pomocą tej metody rozszerza inną.properties
Aby tego uniknąć, musisz podać więcej unikalnych nazw . Moja pierwsza metoda nie ma tego problemu, ponieważ jej fałszywa połowa konstruktora ma unikalną nazwę od klasy.źródło
init
.init
jest często używanym wzorcem i często ma być wywoływany spoza klasy, gdy użytkownik zewnętrzny chce zainicjować, tjvar b = new Thing(); b.init();
. Jest to w 100% stylistyczny wybór, który wolałbym przekazać, że jest to druga funkcja, która jest automatycznie wywoływana przez wykorzystanie wzorców występujących w innych językach. O wiele mniej prawdopodobne jest, że ktoś na to spojrzy i przyjmie, że musi wywołaćMyClass
metodę z zewnątrz, bardziej prawdopodobne, że uświadomi sobie, że intencją jest druga metoda działająca w budownictwie (tj. Wywoływana sama przez instancję).MyClass
.$myvar
standardowy sposób odwoływania się do zmiennych przeznaczonych do przechowywania obiektów jQuery nie jest dogodnie podobny do wzoru posiadania $ na początku zmiennych, do których przywykło tak wielu programistów PHP. Tylko ta mała implikacja, że „tak, to nie jest dokładnie to samo… ale spójrz… to wciąż zmienna, ponieważ w ten sposób zmienne są wykonywane w niektórych językach!” pomagaproperties()
funkcją. Jednak sprawy stają się nieco bardziej skomplikowane przy rozszerzaniu klas, więc sugerowałbym, że jeśli użyjeszproperties()
funkcji we wszystkich swoich klasach, aby zadeklarować swoje właściwości klasy, poprzedź nazwę metody nazwą klasy (IE:MyClassProperties()
aby uniknąć przypadkowego zastąpienia wywołań funkcji w podklasach. Należy również pamiętać, że wszelkie wywołania funkcjisuper()
muszą zostać zadeklarowane jako pierwsze w konstruktorze klasCo z oldschoolowym sposobem?
Konstruktor wymienia tylko te zmienne, które należy obliczyć. Lubię prototypowe dziedziczenie tej funkcji - może pomóc zaoszczędzić dużo pamięci (w przypadku, gdy istnieje wiele nigdy nieprzypisanych zmiennych).
źródło
Jak powiedział Benjamin w swojej odpowiedzi, TC39 wyraźnie postanowił nie uwzględniać tej funkcji przynajmniej w ES2015. Wydaje się jednak, że konsensus zostanie dodany w ES2016.
Składnia nie została jeszcze ustalona, ale istnieje wstępna propozycja dla ES2016, która pozwoli zadeklarować właściwości statyczne w klasie.
Dzięki magii babel możesz tego dzisiaj używać. Włącz transformację właściwości klasy zgodnie z tymi instrukcjami i możesz zacząć. Oto przykład składni:
Ta propozycja jest na bardzo wczesnym etapie, więc przygotuj się na dostosowanie swojej składni w miarę upływu czasu.
źródło
[Długi wątek, nie jestem pewien, czy jest już wymieniony jako opcja ...].
Prostą alternatywą tylko dla kontyngentów byłoby zdefiniowanie stałej poza klasą. Będzie to dostępne tylko z samego modułu, chyba że towarzyszy mu getter.
W ten sposób
prototype
nie jest zaśmiecone i dostajeszconst
.źródło
var
zamiastconst
2) użyjesz wielu wystąpień tej klasy? Następnie zmienne zewnętrzne będą zmieniane z każdym wystąpieniem klasyES7
składnia członka klasy:ES7
ma rozwiązanie do „zbijania” funkcji konstruktora. Oto przykład:Powyższy przykład wyglądałby następująco
ES6
:Korzystając z tej opcji, pamiętaj, że ta składnia może nie być obsługiwana przez wszystkie przeglądarki i może być konieczna transpozycja wcześniejszej wersji JS.
Premia: fabryka obiektów:
źródło
Możesz naśladować zachowanie klas es6 ... i używać zmiennych klas :)
Położyłem to na GitHub
źródło
Sposób, w jaki to rozwiązałem, co jest kolejną opcją (jeśli masz dostępne jQuery), to Definiowanie pól w obiekcie starej szkoły, a następnie rozszerzenie klasy o ten obiekt. Nie chciałem też wysadzać konstruktora zadaniami, co wydawało się być dobrym rozwiązaniem.
- Aktualizacja Po komentarzu Bergi.
Brak wersji JQuery:
Nadal masz konstruktora „grubego”, ale przynajmniej wszystko jest w jednej klasie i przypisane w jednym trafieniu.
EDYCJA 2: Teraz zatoczyłem pełne koło i teraz przypisuję wartości w konstruktorze, np
Dlaczego? Naprawdę proste, korzystając z powyższego i niektórych komentarzy JSdoc, PHPStorm był w stanie wykonać uzupełnianie kodu we właściwościach. Przypisanie wszystkich zmiennych za jednym razem było przyjemne, ale niemożność kodowania uzupełnienia właściwości, imo, nie jest warta (prawie na pewno niewielkiej) poprawy wydajności.
źródło
class
składnię, możesz to zrobićObject.assign
. Nie potrzebujesz jQuery.MyClassFields
konstruktorów - nie ma żadnych metod. Czy potrafisz wyjaśnić, dlaczego to zrobiłeś (zamiast, powiedzmy, prostej funkcji fabrycznej, która zwraca dosłowność obiektu)?Cóż, możesz zadeklarować zmienne w Konstruktorze.
źródło
Nadal nie możesz zadeklarować żadnych klas jak w innych językach programowania. Ale możesz utworzyć tyle zmiennych klas. Ale problemem jest zakres obiektu klasy. Więc według mnie, najlepszy sposób programowania OOP w ES6 Javascript: -
źródło
To jest trochę hackerskie połączenie static i get działa dla mnie
gdzie indziej używane
źródło
singleton_instance
jako nazwę nieruchomości, aby wszyscy rozumieli, co tu robisz.