Jak tworzyć obiekty

0

Hej jestem noobem z js, i chciałem założyć podobny temat jak skończę obecny temat, ale zacząłem pisać bardziej skomplikowany kod i mnie żeby bolą jak widzę co robię.
W projekcie tworze obiekty na 3 główne, pierwszy

export function makeObj(){
   let iAmPrivate = 3.14;
   function doSomething(){return iAmPrivate  * 2}
   return{
      someAction:doSomething
    }

Trochę to żmudne, ale intelisanse sobie z tym radzi, mogę hermetyzować wartości i generalnie mam wszystko pod kontrolą, ale dziedziczenie/reuzywanie jest problematyczne.
Drugim sposobem jest tworzenie klas, niby jest bardziej reuzywalne i niby nie trzeba pamiętać o tych wszystkich dwukropach i przecinkach, ale bolą mnie zęby, jak upubliczniam zmienne, i waru innych sytuacjach też.
Trzecim sposobem są indeksery obj[imStringVar] np do wycięcia i sprawdzenia wybranych pól przed serializacją lub do walidacji wartości od użytkownika. Inteli sens sobie z tym absolutnie nie radzi, za to po napisaniu jest proste w rozumieniu i użyciu, zielonego pojęcia nie mam jak się skaluje.
Idei prototypów nie do końca rozumiem, i trochę się ich boje.

Jak najlepiej w skalowanym sposób tworzyć obiekty w js, lub funkcje. Mam wrażenie że przez ten cały dynamiczny duck typing i brak hermatyzacji na klasach w js absolutnie nie można polegać. Nie rozumiem po co je w prowadzono.

Ed. to juz nawet nie chodzi o samą hermatyzacje, pisze sam więc skrajnych głupot robić nie będę, ale chciałbym ograniczyć problem np. do start, stop, makeStep i zapomnieć o szczegółach implementacjach, ale nie mogę bo muszę całemu światu i IDE, pokazać że mierze dt między każdym krokiem. Z kolei jak korzystam z pierwszego rozwiązania to przy re-używając kod muszę mieć wszystkie szczegóły z tyłu głowy i uważać zeby nic nie namieszać, co też nie specjalnie mi się podoba.

2

Przerzuć się na TypeScript - to jedyna opcja, jeśli zależy Ci na sensownych podpowiedziach w IDE.

1

Albo na Visual Studio Code. Wrzuciłem podany przykład i zrozumiał. Zresztą ogólnie zauważyłem, że od jakiegoś czasu VSC coraz lepiej rozumie kod, nawet ten taki trochę bardziej dynamiczny.

i generalnie mam wszystko pod kontrolą, ale dziedziczenie/reauzywanie jest problematyczne.

Czemu problematyczne? Przy takim podejściu (że tworzymy obiekty za pomocą fabryki) po prostu odpalasz jedną funkcję i doczepiasz do niej rzeczy:

function foo() {
   const self = makeObj();
   self.foo = 123;
}

Czasem tak robię, ale czasem używam klas (w szczególności to jak mam dużo obiektów utworzyć naraz, to klasy/prototypy mogą być lepszym podejściem, bo jedna metoda jest tworzona raz a nie np. w każdym obiekcie od nowa - czyli 10000 obiektów - 10000 razy się odpali i zajmie 10000 razy pamięć)

brak hermatyzacji na klasach w js

Będą prywatne pola w klasach, ale na razie kiepsko ze wsparciem (w Chrome działa, ale gdzie indziej nie za bardzo):
https://kangax.github.io/compat-table/esnext/
(otwórz zakładkę instance class fields)

4
_flamingAccount napisał(a):

Idei protorypów nie do końca rozumiem, i trochę się ich boje.

W takim razie nie rozumiesz w ogóle JS. JS to język z dziedziczeniem prototypowym, jedyny szeroko znany z takim rodzajem dziedziczenia, więc nie można go porównywać z niczym innym. Jeżeli próbujesz w JS programować jak w innych językach przykładowo o zgrozo tak jak w Javie, to robisz to źle.

Jak najlepiej w skalowanym sposób tworzyć obiekty w js, lub funkcje. Mam wrażenie że przez ten cały dynamiczny duck typing i brak hermatyzacji na klasach w js absolutnie nie można polegać. Nie rozumiem po co je w prowadzono.

Klasy to tylko cukier składniowy, który miał przyciągnąć do języka takich jak Ty. Jak widać eksperyment się nie udał, bo to tylko wprowadza ludzi w błąd i próbują w JS programować tak jak w innych językach. Klas w JS się nie używa, tzn. ktoś tam klas używa, ale albo ma doskonałe pojęcie o JS i wie co się dzieje, albo jest noobem i robi to źle. W 90% przypadków to drugie. Wykorzystanie klas generalnie w dłuższej perspektywie może prowadzić do tworzenia niewydajnego kodu. Trzeba naprawdę wiedzieć co się robi, a jeżeli ktoś nie wie co to prototyp, to zupełnie nie wie.

Jedyna w miarę udana adaptacja klas w JS to React, gdzie jako tako to się sprawdziło. Jednak ja nie używam klas w React, można powiedzieć, że zacząłem szeroko używać tej biblioteki dopiero po wprowadzeniu hooków, które dają możliwość pisania kodu bez używania klas.

2
Haskell napisał(a):

Klasy to tylko cukier składniowy, który miał przyciągnąć do języka takich jak Ty. Jak widać eksperyment się nie udał, bo to tylko wprowadza ludzi w błąd i próbują w JS programować tak jak w innych językach. Klas w JS się nie używa, tzn. ktoś tam klas używa, ale albo ma doskonałe pojęcie o JS i wie co się dzieje, albo jest noobem i robi to źle. W 90% przypadków to drugie. Wykorzystanie klas generalnie w dłuższej perspektywie może prowadzić do tworzenia niewydajnego kodu.

kiedyś porównywałem sobie wydajność klas i prototypów i wyszło podobnie.

Chociaż zgadzam się, że masę osób używa klas, bo są przyzwyczajeni i nie rozumieją w prototypy, szczególnie jak przychodzą z C# czy czy innej Javy. To ludzie, którzy tworzą klasę nawet wtedy, kiedy zwykły literał obiektowy by się lepiej nadawał. Zamiast

{foo: 123, bar: 456}

to piszą całą klasę do tego... To trochę jak ci ludzie, którzy poznali destructuring czy ... i używają tego nawet wtedy, kiedy prościej było zrobić bez takich zabaw.

Co do klas natomiast, to nie widzę większych wad. Dla mnie klasy są bardziej czytelne niż pattern typu Foo.prototype = function (){} i łatwiej się poddają statycznej analizie kodu. Więc klasy = ok.

Przy czym rzadko używam klas do dziedziczenia (kiedyś jeszcze z React.Component, ale hooki są wygodniejsze), zwykle klasy po prostu oznaczają tyle, że zbiorę w jednym miejscu metody, jakie ma mieć obiekt i tyle i raczej rzadko w ogóle wpadam na pomysł dziedziczenia, bo po
co (czasem może się to przydać takie klasyczne dziedziczenie, ale to rzadkie przypadki. Nawet klasy z React w zasadzie bardziej pod implementację interfejsu podchodzą niż pod dziedziczenie klasowe. No, pomijając setState).

Co prawda zdarza mi się robić mixiny w stylu Object.assign({}, foo, bar), co też można uznać za formę dziedziczenia. No i kiedyś Object.create używałem do dziedziczenia prototypowego, ale ostatnio przestałem to robić. Ale jak dziedziczę to raczej prędzej głupie obiekty (tj. pojemniki na dane). I dziedziczenie następuje w runtime (co też jest różnicą pewną. Dziedziczę, żeby przekopiować istniejący w pamięci obiekt, żeby mieć kopię obiektu i działać na kopii obiektu, a nie po to, żeby reużywać kod na poziomie jego pisania).

1
_flamingAccount napisał(a):

Hej jestem noobem z js, i chciałem założyć podobny temat jak skończę obecny temat, ale zacząłem pisać bardziej skomplikowany kod i mnie żeby bolą jak widzę co robię.
W projekcie tworze obiekty na 3 głowne, pierwszy

"na 3 główne"?

export function makeObj(){
   let iAmPrivate = 3.14;
   function doSomething(){return iAmPrivate  * 2}
   return{
      someAction:doSomething
    }

Trochę to żmudne, ale intelisanse sobie z tym radzi, mogę hermetyzować wartości i generalnie mam wszystko pod kontrolą, ale dziedziczenie/reauzywanie jest problematyczne.

Co masz na myśli, pisząc, że jest "problematyczne"?

Drugim sposobem jest tworzenie klas, niby jest bardziej reuzywalne i niby nie trzeba pamiętać o tych wszystkich dwukropach i przecinkach, ale bolą mnie zęby, jak upubliczniam zmienne, i waru innych sytuacjach też.

W jakich innych sytuacjach? Może naprawdę nie ma aż tak wielu wad, jak Ci się wydaje?

Trzecim sposobem są indeksery obj[imStringVar] np do wycięcia i sprawdzenia wybranych pól przed serializacją lub do walidacji wartości od użytkownika. Inteli sens sobie z tym absolutnie nie radzi, za to po napisaniu jest proste w rozumieniu i użyciu, zielonego pojecia nie mam jak się skaluje.

Co rozumiesz przez "indeksery"?

Jak najlepiej w skalowanym sposób tworzyć obiekty w js, lub funkcje. Mam wrażenie że przez ten cały dynamiczny duck typing i brak hermatyzacji na klasach w js absolutnie nie można polegać. Nie rozumiem po co je w prowadzono.

Zależy, co rozumiesz przez "poleganie". Jeśli oczekujesz, że JavaScript będzie działać tak samo jak np. C#, może być ciężko – ale z drugiej strony gdyby działał tak samo, używałbyś C#, a nie JavaScript.

Ed. to juz nawet nie chodzi o samą hermatyzacje, pisze sam więc skrajnych głupot robić nie będę, ale chciałbym ograniczyć problem np. do start, stop, makeStep i zapomnieć o szczegółach implementacjach, ale nie mogę bo muszę całemu światu i IDE, pokażać że mierze dt między każdym krokiem.

Z kolei jak korzystam z pierwszego rozwiązania to przy re-używając kod musze mieć wszystkie szczegóły z tyłu głowy i uważać zeby nic nie namieszac, co też nie specjalnie mi się podoba.

Też ten problem miałem. To był główny mój powód rozpoczęcia używania TypeScript*; jakoś to idzie. Choć nie jestem pewien, czy sensownie używając prototypów – i będąc do nich przyzwyczajonym – nie można by tego samego osiągnąć w JavaScripcie, co mam w TypeScripcie.**


PS. ** Piszę tu bardziej o wrażeniu z korzystania niż o możliwościach.


PS2. * W sumie jednak nie wiem, czy to był główny powód (a nawet, czy to rozsądny powód).

0

Albo na Visual Studio Code. Wrzuciłem podany przykład i zrozumiał

Co masz na myśli, pisząc, że jest "problematyczne"?

Załóżmy że chce napisać substytut api do sterowania urządzeniem od oświetlenia i tworze go fukcją(fabryką). W zależności widzi mi się chce korzystać z mocka lub fizycznego urzadenia. Obie wersje mają ten sam interfejs 1:1. Wiec pisze fabrykę(jako wzorzec), ale w takiej sytuacji intelisens sobie nie radzi(co jest zrozumiałe). Mam zmocowane w ten sposób wszystkie zależności w projekcie, co jak na razie jest wygodne bo większość appki piszę w konsoli, i tylko od czasu do czasu sprawdzam czy się integruje.
Pisząc to naszło mnie zamiast 2 rożnych obiektów mógłby, zwracać adapter z który intelisense by już łyknął, ale to prawie pisanie wraperów na wrapery i takie sytuacje uważałem za problematyczne. Chociaż to może kwestia praktyki i wyczucia.

function getDevice(){
    on:()=>{/*logika*/}
    off:()=>{/*logika*/}
}
function deviceFactory(){
    if(Environment.type === "mock")
       rerurn getDevice();
    if(Environment.type === "real")
       rerurn new manufacturerApi();
}

W jakich innych sytuacjach? Może naprawdę nie ma aż tak wielu wad, jak Ci się wydaje?

Zależy, co rozumiesz przez "poleganie". Jeśli oczekujesz, że JavaScript będzie działać tak samo jak np. C#, może być ciężko – ale z drugiej strony gdyby działał tak samo, używałbyś C#, a nie JavaScript.

Moim oczekiwaniem, było to że mechnizm klas o którym sie dowiedziałem, zapewni zbliżone możliwości mechanizmu fukcji/fakbryk, a tak nie jest(przynajmniej nie jestem tego swiadomy) i to najprawdopodobniej było źródłem irytacji.

Problem który chciałem rozwiązać dziedziczeniem to książkowy przykład. Mam na pisaną cała infrastrukturę do sterowania przełącznikiem i jako użytkownik chciałbym zlecić mu jakieś zadania, wykonywane co pewnie czas. Włącz jeśli wartość x jest poniżej, wyłącz jeśli po wyżej, itp, itd. Zadania same w sobie są proste, ale obsługa błędów, zwalnianie zasobów itd. są nie trywialne i fajnie było by to zrobić tylko raz. Klasyka gatunku.

Chyba zrobie to albo przez prototypy, albo przez odpowiednią kompozycje w funkcjach(fabrykach).

0

Załóżmy że chce napisać substytut api do sterowania urządzeniem od oświetlenia i tworze go fukcją(fabryką). W zależności widzi mi się chce korzystać z mocka lub fizycznego urzadenia. Obie wersje mają ten sam interfejs 1:1. Wiec pisze fabrykę(jako wzorzec), ale w takiej sytuacji intelisens sobie nie radzi(co jest zrozumiałe).

Co to znaczy, że "nie radzi sobie"? Piszesz ogólnie – podaj może przykłady "rzeczy" (jak masz pod ręką), które chciałbyś, by były podpowiadane przez IntelliSense w VS Code, a nie są.

W jakich innych sytuacjach? Może naprawdę nie ma aż tak wielu wad, jak Ci się wydaje?

Zależy, co rozumiesz przez "poleganie". Jeśli oczekujesz, że JavaScript będzie działać tak samo jak np. C#, może być ciężko – ale z drugiej strony gdyby działał tak samo, używałbyś C#, a nie JavaScript.

Moim oczekiwaniem, było to że mechnizm klas o którym sie dowiedziałem, zapewni zbliżone możliwości mechanizmu fukcji/fakbryk, a tak nie jest(przynajmniej nie jestem tego swiadomy) i to najprawdopodobniej było źródłem irytacji.

Nie znam się na wzorcu fabryki – czytałem o nim, ale niezbyt łapię ideę, choć może nawet już go od dawna stosuję – ale zapytam: co rozumiesz przez "zbliżone możliwości"?

0

Co to znaczy, że "nie radzi sobie"? Piszesz ogólnie – podaj może przykłady "rzeczy" (jak masz pod ręką), które chciałbyś, by były podpowiadane przez IntelliSense w VS Code, a nie są.

Chciałbym mieć wylistowane, możliwe do wywołania metody. W przykładzie wyżej to on, off.

Określenia fukcji/fakbryk w znaczeniu takim jak @LukeJL użył fabryka, czyli

function makeObj(){
   function sayHello(){console.log('hello word'}
   return{
        greeting:sayHello
   }
}
1

To

function getDevice(){
    on:()=>{/*logika*/}
    off:()=>{/*logika*/}
}

nie jest poprawny JavaScript (patrz "update" *). Możesz zrobić tak:

function getDevice(){
    return {
        on: () => {/*logika*/},
        off: () => {/*logika*/}
    };
}
const gd = getDevice(); // Nowy obiekt reprezentujący urządzenie?

albo tak:

function getDevice(){
    this.on = () => {/*logika*/};
    this.off = () => {/*logika*/};
}
const gd = new getDevice(); // Nowy urządzeniobracz?

W obu przypadkach przy użyciu zmiennej gd VS Code podpowiada, jakie ma ona składowe. Czy o to Ci chodzi?


PS. Oczywiście mój VS Code. Może to zależne od konfiguracji, ja wiem? Ja staram się polegać na wartościach domyślnych, ale coś tam zmieniałem swojego czasu.


UPDATE: * Jednak to wydaje się być poprawnym JavaScriptem dla VS Code – mówi on, że on oraz off są w nim etykietami. Czy to zachowanie zgodne ze standardem – nie wiem.

2

Najlepiej jakbyś poczytał jednak o tych prototypach. Nie wiem czy wiesz, ale produkując obiekty w ten sposób jak wyżej w tej swojej fabryce możesz duplikować kod w pamięci czyli jak będziesz miał tysiąc obiektów z jakimiś funkcjami w środku to każdy obiekt będzie miał osobną kopię tych funkcji. Tworzenie instancji prototypu już rozwiązuje ten problem, bo wtedy tylko prototyp trzyma kopię funkcji. Klasy są właśnie złe z tego powodu, że w niektórych sytuacjach też powodują duplikowanie i jeżeli nie wiesz precyzyjnie jak JS działa pod spodem, to narażasz się na to. Jak masz prostą klasę to nie ma problemu, ale gdy zadeklarujesz metodę w konstruktorze albo zaczniesz się bawić bind to już mogą się dziać rzeczy o których osoba nieznająca prototypów nie ma pojęcia.

Btw. jest taka książka JavaScript: The good parts (bardzo cienka) oraz późniejsze wykłady jej autora, które sporo podpowiadają odnośnie tego z czego warto/powinno się korzystać w JS, a których ficzerów języka się wystrzegać.

1

@Silv:
Dzięki! Rzeczywiście działa, problem z intelisensem u mnie polegał na tym ze korzystałem z singletonów(takich klasyczny ze sprawdzeniem nulla) w mocach na storage itp. Usunięcie ich naprawiło problem.

@Haskell
Akurat na 98% będe miał cały czas malutko obiektów ale dobrze wiedzieć. Chyba tak zrobie i przeczytam :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1