Co to jest obiekt w javascript. Tworzenie konstruktora obiektów

Obiekt to nieuporządkowany zbiór właściwości. Właściwość jest częścią obiektu symulującą zmienną. Właściwość składa się z nazwy i wartości.

W JavaScript istnieją trzy kategorie obiektów:

  • Obiekty typu podstawowego są obiektami zdefiniowanymi w specyfikacji ECMAScript. Na przykład obiekty typu Array, Function, Date lub RegExp są obiektami typu podstawowego.
  • Obiekty wykonawcze to obiekty zdefiniowane w środowisku wykonawczym (takim jak przeglądarka). Na przykład obiekty typu HTMLElement są obiektami wykonawczymi.
  • Obiekty niestandardowe to dowolny obiekt powstały w wyniku wykonania kod programu JavaScript.

Tworzenie obiektu

Obiekt można utworzyć za pomocą literału obiektowego lub operatora new za pomocą konstruktora.

Literał obiektowy to rozdzielona przecinkami lista zera lub większej liczby właściwości (nazwa: pary wartości) ujęta w nawiasy klamrowe. Nazwa właściwości może być dowolnym prawidłowym identyfikatorem, literałem ciągu (dozwolony jest pusty ciąg znaków) lub liczbą. Numeryczne nazwy właściwości są automatycznie konwertowane na ciągi znaków. Wartość właściwości może być wartością dowolnego typu lub wyrażeniem (wartość właściwości w tym przypadku będzie wynikiem oceny wyrażenia):

// Utwórz pusty obiekt var o = (); // Utwórz obiekt z trzema właściwościami var user = ( name: "Homer", "age": 45, 1: true );

Tworzenie obiektu za pomocą operatora new:

Var o = nowy obiekt();

Operacje na obiekcie

Główne operacje wykonywane na obiektach to dodawanie nowych właściwości, zmiana istniejących właściwości, usuwanie właściwości i uzyskiwanie dostępu do właściwości.

Możesz dodać nową właściwość do obiektu, przypisując wartość do właściwości. Aby przypisać wartość do właściwości, należy uzyskać do niej dostęp. Aby uzyskać dostęp do właściwości, użyj jednego z operatorów dostępu: . (kropka) lub (nawiasy kwadratowe):

Dostęp do właściwości i zmiana jej wartości odbywa się dokładnie w ten sam sposób (za pomocą operatorów dostępu):

Varo = (x:5); alert(o.x); // Dostęp do właściwości alert(o["x"]); // Dostęp do właściwości o.x = 10; // Zmień wartość

Usuwanie właściwości odbywa się za pomocą operatora usuwania:

Varo = (x:5); alert("x" w o); // prawda usuń o.x; alert("x" w o); // FAŁSZ

Aby iterować po właściwościach obiektu, użyj pętli for-in:

Var obj = (x: 5, y: 10, str: "Cześć!"); for (var prop in obj) ( alert(prop); )

Metody obiektowe

Właściwość, której wartością jest funkcja, nazywa się metodą. Wywołanie metody odbywa się analogicznie jak wywołanie zwykłej funkcji – za pomocą operatora () (operatora wywołania):

Var o = ( powiedzHi: funkcja() ( alert("Witam!"); ) ); o.powiedz Cześć(); // "Cześć!"

Aby uzyskać dostęp do właściwości obiektu wewnątrz metody, użyj this słowa kluczowego. Zawiera referencję do obiektu, za pomocą którego wywołano metodę:

Var o = ( nazwa: "Homer", powiedzNazwa: funkcja() ( alert(to.imie); ) ); o.powiedzNazwa(); // „Homer”

Zamiast słowa kluczowego this możesz bezpośrednio użyć nazwy obiektu, ale nie jest to zbyt wygodne, ponieważ jeśli zmieni się nazwa obiektu, będziesz musiał także zmienić nazwę w metodach:

Var o = ( nazwa: "Homer", powiedzName: funkcja() ( alert(o.imie); ) ); o.powiedzNazwa(); // „Homer”

Ostatnia aktualizacja: 04.08.2018

Programowanie obiektowe jest obecnie jednym z dominujących paradygmatów w tworzeniu aplikacji, a w JavaScript możemy również w pełni wykorzystać OOP. Jednocześnie w odniesieniu do JavaScriptu programowanie obiektowe ma pewne cechy.

Obiekty

W poprzednich tematach pracowaliśmy z prymitywnymi danymi - liczbami, ciągami znaków, ale dane nie zawsze reprezentują typy pierwotne. Na przykład, jeśli w naszym programie będziemy musieli opisać istotę osoby, która ma imię, wiek, płeć itd., to naturalnie nie będziemy w stanie przedstawić istoty osoby w postaci liczby lub ciągu znaków. Będziemy potrzebować kilku linii lub liczb, aby właściwie opisać osobę. Pod tym względem osoba będzie działać jako złożona złożona struktura, która będzie miała indywidualne właściwości - wiek, wzrost, imię, nazwisko itp.

Do pracy z takimi strukturami JavaScript używa . Każdy obiekt może przechowywać właściwości opisujące jego stan i metody opisujące jego zachowanie

Tworzenie nowego obiektu

Istnieje kilka sposobów na utworzenie nowego obiektu.

Pierwszy sposób polega na użyciu konstruktora Object:

Użytkownik Var = nowy obiekt();

W tym przypadku obiekt nazywany jest użytkownikiem. Jest ona definiowana w taki sam sposób jak każda zwykła zmienna za pomocą słowa kluczowego var.

Nowe wyrażenie Object() reprezentuje wywołanie konstruktora, czyli funkcji, która tworzy nowy obiekt. Aby wywołać konstruktora, użyj nowego operatora. Wywołanie konstruktora przypomina w zasadzie wywołanie zwykłej funkcji.

Drugi sposób tworzenia obiektu polega na użyciu nawiasów klamrowych:

Var użytkownik = ();

Dziś druga metoda jest bardziej powszechna.

Właściwości obiektu

Po utworzeniu obiektu możemy zdefiniować na nim właściwości. Aby zdefiniować właściwość należy po nazwie obiektu podać nazwę właściwości oddzieloną kropką i przypisać jej wartość:

Var użytkownik = (); user.name = "Tomek"; wiek użytkownika = 26;

W tym przypadku deklarowane są dwie właściwości: nazwa i wiek, którym przypisuje się odpowiednie wartości. Następnie możemy wykorzystać te właściwości, np. wyświetlić ich wartości w konsoli:

Console.log(nazwa.użytkownika); konsola.log(wiek użytkownika);

Właściwości można także zdefiniować podczas definiowania obiektu:

Var użytkownik = (imię: „Tomek”, wiek: 26 );

W tym przypadku do przypisania wartości właściwości używany jest dwukropek, a po definicji właściwości umieszczany jest przecinek (a nie średnik).

Ponadto istnieje skrótowy sposób definiowania właściwości:

Nazwa zmiennej = „Tom”; var wiek = 34; var użytkownik = (imię, wiek); konsola.log(nazwa.użytkownika); // Tom console.log(user.age); // 34

W tym przypadku nazwy zmiennych są jednocześnie nazwami właściwości obiektu. W ten sposób możesz tworzyć bardziej złożone projekty:

Nazwa zmiennej = „Tom”; var wiek = 34; var użytkownik = (imię, wiek); var nauczyciel = (użytkownik, kurs: „JavaScript”); konsola.log(nauczyciel.użytkownik); // (imię: „Tomek”, wiek: 34) console.log(teacher.course); // JavaScript

Metody obiektowe

Metody obiektu definiują jego zachowanie lub akcje, które wykonuje. Metody to funkcje. Na przykład zdefiniujmy metodę, która wyświetli imię i wiek osoby:

Var użytkownik = (); user.name = "Tomek"; wiek użytkownika = 26; użytkownik.display = funkcja())( konsola.log(nazwa.użytkownika); konsola.log(wiek użytkownika); ); // wywołanie metody user.display();

Podobnie jak w przypadku funkcji, metody są najpierw definiowane, a następnie wywoływane.

Metody można również definiować bezpośrednio podczas definiowania obiektu:

Var użytkownik = ( imię: „Tomek”, wiek: 26, wyświetlacz: funkcja())( console.log(this.name); console.log(this.age); ) );

Podobnie jak w przypadku właściwości, metodzie przypisuje się odwołanie do funkcji za pomocą dwukropka.

Aby uzyskać dostęp do właściwości lub metod obiektu w tym obiekcie, użyj this słowa kluczowego. Oznacza odniesienie do bieżącego obiektu.

Możesz także użyć skrótu do zdefiniowania metod, pomijając dwukropek i funkcję:

Var użytkownik = (imię: „Tomek”, wiek: 26, display())( console.log(this.name, this.age); ), move(place)( console.log(this.name, „idzie do " , miejsce); użytkownik.wyświetlacz(); // Tom 26 user.move("sklep"); //Tom idzie do sklepu

Składnia tablicy

Jest również alternatywny sposób definiowanie właściwości i metod przy użyciu składni tablicowej:

Var użytkownik = (); użytkownik["imię"] = "Tomek"; użytkownik["wiek"] = 26; użytkownik["wyświetlacz"] = funkcja())( konsola.log(nazwa.użytkownika); konsola.log(wiek użytkownika); ); // wywołanie metody user["display"]();

Nazwę każdej właściwości lub metody ujęto w cudzysłów i nawiasy kwadratowe, wówczas przypisano jej także wartość. Na przykład użytkownik["wiek"] = 26 .

Uzyskując dostęp do tych właściwości i metod, możesz użyć notacji z kropką (user.name) lub użyć: user["name"]

Ciągi jako właściwości i metody

Należy również zauważyć, że nazwy właściwości i metod obiektów są zawsze ciągami znaków. Oznacza to, że moglibyśmy przepisać poprzednią definicję obiektu w następujący sposób:

Var użytkownik = ( "imię": "Tomek", "wiek": 26, "display": funkcja())( konsola.log(nazwa.użytkownika); konsola.log(wiek użytkownika); ) ); // wywołanie metody user.display();

Z jednej strony nie ma różnicy między tymi dwiema definicjami. Z drugiej strony zdarzają się przypadki, w których pomocne może być zamknięcie tytułu w linii. Na przykład, jeśli nazwa właściwości składa się z dwóch słów oddzielonych spacją:

Var użytkownik = ( imię: „Tom”, wiek: 26, „pełne imię i nazwisko”: „Tom Johns”, „informacje o wyświetlaczu”: funkcja())( console.log(nazwa.użytkownika); console.log(użytkownik.wiek ) ; ) ); konsola.log(użytkownik["pełne imię i nazwisko"]); użytkownik["wyświetl informacje"]();

Tylko w tym przypadku, aby uzyskać dostęp do takich właściwości i metod, musimy zastosować składnię tablicową.

Usuwanie właściwości

Powyżej przyjrzeliśmy się, jak możemy dynamicznie dodawać nowe właściwości do obiektu. Możemy jednak również usuwać właściwości i metody za pomocą operatora usuwania. I podobnie jak przy dodawaniu, właściwości możemy usuwać na dwa sposoby. Pierwszym sposobem jest użycie notacji z kropką:

Usuń obiekt.właściwość

Lub użyj składni tablicowej:

Usuń obiekt["właściwość"]

Na przykład usuńmy właściwość:

Var użytkownik = (); user.name = "Tomek"; wiek użytkownika = 26; użytkownik.display = funkcja())( konsola.log(nazwa.użytkownika); konsola.log(wiek użytkownika); ); konsola.log(nazwa.użytkownika); // Tomek usuń nazwę użytkownika; // usuń właściwość // Alternatywna opcja// usuń użytkownika["nazwa"]; konsola.log(nazwa.użytkownika); // nieokreślony

Po usunięciu właściwość będzie niezdefiniowana, więc przy próbie uzyskania dostępu do niej program zwróci wartość niezdefiniowaną.

JavaScript został zaprojektowany w oparciu o prosty paradygmat obiektowy. Obiekt jest zbiorem właściwości, a właściwość jest powiązaniem pomiędzy nazwą (lub klucz) i wartość. Wartością właściwości może być funkcja, w tym przypadku właściwość nazywana jest metodą. Oprócz obiektów predefiniowanych w przeglądarce, możesz definiować własne obiekty. W tym rozdziale opisano sposób korzystania z obiektów, właściwości i funkcji i metody oraz sposoby tworzenia własnych obiektów.

Przegląd obiektów

Obiekty w JavaScript, podobnie jak w wielu innych językach programowania, można porównać do obiektów w prawdziwym życiu. Koncepcję obiektów w JavaScript można zrozumieć w odniesieniu do rzeczywistych, namacalnych obiektów.

W JavaScript obiekt jest samodzielną jednostką z właściwościami i typem. Porównaj to na przykład z filiżanką. Filiżanka to przedmiot, który ma właściwości. Kubek ma kolor, wzór, wagę, materiał, z którego jest wykonany itp. W ten sam sposób obiekty JavaScript mogą mieć właściwości, które definiują ich charakterystykę.

Obiekty i właściwości

Z obiektem JavaScript powiązane są właściwości. Właściwość obiektu można wyjaśnić jako zmienną związaną z obiektem. Właściwości obiektów są w zasadzie takie same jak zwykłe zmienne JavaScript, z wyjątkiem dołączenia do obiektów. Właściwości obiektu definiują cechy obiektu. Dostęp do właściwości obiektu można uzyskać za pomocą prostego zapisu z kropką:

NazwaObiektu.NazwaWłaściwości

Podobnie jak wszystkie zmienne JavaScript, zarówno nazwa obiektu (która może być zwykłą zmienną), jak i nazwa właściwości uwzględniają wielkość liter. Możesz zdefiniować właściwość, przypisując jej wartość. Na przykład utwórzmy obiekt o nazwie myCar i nadajmy mu właściwości o nazwach make, model i rok w następujący sposób:

Var myCar = nowy obiekt(); myCar.make = "Ford"; myCar.model = "Mustang"; mójsamochód.rok = 1969; myCar.color; // nieokreślony

Dostęp do właściwości obiektów JavaScript można również uzyskać lub ustawić za pomocą notacji nawiasowej (więcej szczegółów można znaleźć w sekcjach metody dostępu do właściwości). Czasami nazywane są obiekty tablice asocjacyjne, ponieważ każda właściwość jest powiązana z wartością ciągu, za pomocą której można uzyskać do niej dostęp. Na przykład możesz uzyskać dostęp do właściwości obiektu myCar w następujący sposób:

Mój Samochód["make"] = "Ford"; myCar["model"] = "Mustang"; mójsamochód["rok"] = 1969;

Nazwą właściwości obiektu może być dowolny prawidłowy ciąg znaków JavaScript lub dowolny element, który można przekonwertować na ciąg znaków, włączając ciąg pusty. Jednak dostęp do dowolnej nazwy właściwości, która nie jest prawidłowym identyfikatorem JavaScript (na przykład nazwy właściwości zawierającej spację lub łącznik lub rozpoczynającej się od liczby), można uzyskać wyłącznie za pomocą nawiasu kwadratowego. Notacja ta jest również bardzo użyteczna, gdy nazwy właściwości mają być określane dynamicznie (kiedy nazwa właściwości jest ustalana dopiero w czasie wykonywania). Przykłady są następujące:

// za jednym razem tworzone i przypisywane są cztery zmienne, // oddzielane przecinkami var myObj = new Object(), str = "myString", rand = Math.random(), obj = new Object(); myObj.type = "Składnia kropki"; myObj["data utworzenia"] = "Ciąg ze spacją"; myObj = "Wartość ciągu"; myObj = "Liczba losowa"; myObj = "Obiekt"; myObj[""] = "Nawet pusty ciąg znaków"; konsola.log(myObj);

Należy pamiętać, że wszystkie klucze w notacji nawiasów kwadratowych są konwertowane na ciągi znaków, chyba że są symbolami, ponieważ nazwy właściwości obiektów JavaScript (klucze) mogą być tylko ciągami znaków lub symbolami (w pewnym momencie nazwy prywatne zostaną również dodane jako propozycja pól klas postępuje, ale nie będziesz ich używać z formą). Na przykład w powyższym kodzie, gdy klucz obj zostanie dodany do myObj , JavaScript wywoła metodę obj.toString() i użyje tego ciągu wynikowego jako nowego klucza.

Dostęp do właściwości można także uzyskać za pomocą wartości ciągu znaków przechowywanej w zmiennej:

Var nazwa_właściwości = "make"; myCar = "Ford"; nazwawłaściwości = "model"; myCar = "Mustang";

Korzystanie z funkcji konstruktora

Alternatywnie możesz utworzyć obiekt, wykonując te dwa kroki:

  1. Zdefiniuj typ obiektu pisząc funkcję konstruktora. Istnieje silna konwencja, nie bez powodu, aby używać dużej litery początkowej.
  2. Utwórz instancję obiektu za pomocą nowego pliku .

Aby zdefiniować typ obiektu, utwórz funkcję dla typu obiektu, która określi jego nazwę, właściwości i metody. Załóżmy na przykład, że chcesz utworzyć typ obiektu dla samochodów. Chcesz, aby tego typu obiekt nazywał się Car , A ty chcesz, aby miał właściwości marki, modelu i roku. Aby to zrobić, napisz następującą funkcję:

Funkcja Samochód(marka, model, rok) ( this.make = marka; this.model = model; this.year = rok; )

Zwróć uwagę na użycie this do przypisania wartości do właściwości obiektu na podstawie wartości przekazanych do funkcji.

Teraz możesz utworzyć obiekt o nazwie mycar w następujący sposób:

Var mycar = nowy samochód („Eagle”, „Talon TSi”, 1993);

Ta instrukcja tworzy mycar i przypisuje mu określone wartości jego właściwości. Następnie wartością mycar.make jest ciąg znaków „Eagle”, mycar.year jest liczbą całkowitą 1993 i tak dalej.

Możesz utworzyć dowolną liczbę obiektów Car poprzez wywołanie new . Na przykład,

Var kenscar = nowy samochód („Nissan”, „300ZX”, 1992); var vpgscar = nowy samochód("Mazda", "Miata", 1990);

Obiekt może mieć właściwość, która sama w sobie jest innym obiektem. Załóżmy na przykład, że definiujesz obiekt zwany osobą w następujący sposób:

Funkcja Osoba(imię, wiek, płeć) ( this.name = imię; this.age = wiek; this.sex = płeć; )

a następnie utwórz instancję dwóch nowych obiektów osoby w następujący sposób:

Var rand = nowa osoba("Rand McKinnon", 33, "M"); var ken = nowa osoba("Ken Jones", 39, "M");

Następnie możesz przepisać definicję Car, aby uwzględnić właściwość właściciela, która przyjmuje obiekt osoby, w następujący sposób:

Funkcja Samochód(marka, model, rok, właściciel) ( this.make = marka; this.model = model; this.year = rok; this.owner = właściciel; )

Aby utworzyć instancję nowych obiektów, użyj następujących poleceń:

Var car1 = nowy samochód („Eagle”, „Talon TSi”, 1993, rand); var car2 = nowy samochód("Nissan", "300ZX", 1992, ken);

Zauważ, że zamiast przekazywać dosłowny ciąg znaków lub wartość całkowitą podczas tworzenia nowych obiektów, powyższe instrukcje przekazują obiekty Rand i Ken jako argumenty właścicieli. Następnie, jeśli chcesz poznać nazwisko właściciela samochodu2, możesz uzyskać dostęp do następującej właściwości:

Nazwa.właściciela.samochodu2

Pamiętaj, że zawsze możesz dodać właściwość do wcześniej zdefiniowanego obiektu. Na przykład oświadczenie

Car1.color = "czarny";

dodaje właściwość color do car1 i przypisuje jej wartość „black”. Nie ma to jednak wpływu na żadne inne obiekty. Aby dodać nową właściwość do wszystkich obiektów tego samego typu, należy dodać tę właściwość do definicji typu obiektu Samochód.

Korzystanie z metody Object.create

Zobacz też

  • Aby zagłębić się w szczegóły, przeczytaj o szczegółach modelu obiektowego JavaScript.
  • Aby poznać klasy ECMAScript 2015 (nowy sposób tworzenia obiektów), przeczytaj rozdział Klasy JavaScript.

Obiekty są podstawą JavaScript. Wiele wbudowanych typów danych jest reprezentowanych jako obiekty. Aby odnieść sukces jako programista JavaScript, musisz dobrze zrozumieć, jak one działają. Elementy składowe obiektu nazywane są jego polami lub właściwościami obiektu JavaScript. Służą do opisu dowolnego aspektu obiektu. Właściwość może opisywać długość listy, kolor nieba lub datę urodzenia danej osoby. Tworzenie obiektów jest łatwym procesem. Język zapewnia składnię zwaną literałami obiektowymi, które są oznaczone nawiasami klamrowymi.

Dostęp do właściwości

Język zapewnia dwa wpisy umożliwiające dostęp do właściwości. Pierwsza i najczęstsza znana jest jako notacja kropkowa. W notacji kropkowej dostęp do zasobu można uzyskać, podając nazwę obiektu hosta, po której następuje kropka i nazwa właściwości. Na przykład, gdy obiekt.foo był początkowo ustawiony na jeden, jego wartość zmieni się na 2 po wykonaniu instrukcji JavaScript object.

Alternatywna składnia dostępu jest znana jako notacja nawiasowa. W zapisie po nazwie obiektu następuje zestaw nawiasów kwadratowych. W nich nazwa właściwości jest określona jako ciąg znaków:

obiekt["foo"] = obiekt["foo"] + 1.

Jest bardziej wyrazisty niż zapis z kropką, ponieważ pozwala zmiennej określić całość lub część nazwy właściwości. Jest to możliwe, ponieważ interpreter obiektów JavaScript automatycznie konwertuje to wyrażenie na ciąg znaków, a następnie uzyskuje odpowiednią właściwość. Nazwy właściwości tworzone są na bieżąco poprzez połączenie zawartości zmiennej f z ciągiem „oo”:

obiekt = „pasek”.

Notacja w nawiasach pozwala, aby nazwy właściwości zawierały znaki zabronione w notacji z kropką. Na przykład poniższe stwierdzenie w nawiasach jest całkowicie legalne. Jeśli jednak użytkownik spróbuje utworzyć tę samą nazwę właściwości w notacji kropkowej, napotka błąd składniowy:

obiekt["!@#$% &*()."] = prawda.

Dostęp do właściwości zagnieżdżonych obiektów JavaScript można uzyskać, łącząc kropki i/lub nawiasy. Na przykład następujący obiekt zawiera zagnieżdżony obiekt o nazwie baz, który zawiera inny obiekt o nazwie foo, który ma właściwość o nazwie bar zawierającą wartość pięć:

var obiekt = ( baz: ( foo: ( bar: 5 ) ) ).

Poniższe wyrażenia uzyskują dostęp do właściwości dołączonego paska. W pierwszym wyrażeniu zastosowano notację kropkową, natomiast w drugim wyrażeniu zastosowano notację kwadratową. Trzecie wyrażenie łączy oba wpisy, aby osiągnąć ten sam wynik:

  • obiekt.baz.foo.bar;
  • obiekt["baz"]["foo"]["bar"];
  • obiekt["baz"].foo["bar"].

Wyrażenia takie jak te pokazane w poprzednim przykładzie mogą spowodować pogorszenie wydajności, jeśli zostaną użyte nieprawidłowo i spowodować awarię obiektu JavaScript. Ocena każdego wyrażenia z kropką lub nawiasem wymaga czasu. Jeśli ta sama właściwość jest używana wielokrotnie, sensowne jest jednorazowe uzyskanie dostępu do właściwości, a następnie zapisanie wartości w zmiennej lokalnej na potrzeby wszystkich przyszłych zastosowań.

Funkcja jako metoda

Kiedy funkcja jest używana jako właściwość obiektu, nazywa się ją metodą. Podobnie jak właściwości, są one określone w notacji dosłownej obiektu. Na przykład:

var obiekt = ( suma: funkcja (foo, bar) ( return foo + bar; ) ).

Metody obiektowe JavaScript można wywoływać za pomocą znaków i nawiasów. Poniższy przykład wywołuje metodę sum() z poprzedniego przykładu, używając obu wpisów:

  • obiekt.suma(1, 2);
  • obiekt["suma"](1, 2).

Notacja dosłowna obiektów jest przydatna do tworzenia nowych obiektów, ale nie pozwala dodawać właściwości ani metod do już istniejących. Na szczęście dodanie nowych danych jest tak proste, jak utworzenie instrukcji przypisania. Tworzony jest pusty obiekt. Następnie za pomocą operatorów przypisania dodajemy dwie właściwości foo i bar oraz metodę baz:

  • var obiekt = ();
  • obiekt.foo = 1;
  • obiekt.bar = null;
  • obiekt.baz = funkcja() (zwróć „witaj z baz()”; ).

Enkapsulacja programu

Podstawową ideą programowania obiektowego jest podzielenie programów na mniejsze części i przypisanie każdej części odpowiedzialnej za zarządzanie własnym stanem. Zatem pewna wiedza na temat działania części programu może dotyczyć tej części. Osoba pracująca nad resztą programu nie powinna o tym pamiętać ani nawet wiedzieć. Ilekroć te dane lokalne się zmieniają, tylko kod bezpośrednio wokół nich wymaga aktualizacji.

Różne części takiego programu komunikują się ze sobą poprzez interfejsy, ograniczone zestawy funkcji lub powiązania, które zapewniają użyteczną funkcjonalność na bardziej abstrakcyjnym poziomie, ukrywając jednocześnie ich dokładną implementację. Takie części programu modeluje się za pomocą obiektów. Ich interfejs składa się z określonego zestawu metod i właściwości. Właściwości będące częścią interfejsu nazywane są publicznymi. Pozostałe, które nie powinny dotykać kodu zewnętrznego, nazywane są prywatnymi.

Wiele języków zapewnia możliwość rozróżnienia właściwości publicznych od prywatnych i nie pozwala kodowi zewnętrznemu na dostęp do prywatnych. JavaScript, ponownie przyjmujący podejście minimalistyczne, jeszcze nie istnieje. Obecnie trwają prace nad dodaniem tego języka. Dlatego programiści JavaScriptu z powodzeniem wykorzystają ten pomysł. Zazwyczaj dostępny interfejs jest opisany w dokumentacji lub komentarzach. Powszechną praktyką jest również umieszczanie znaku podkreślenia (_) na początku nazw właściwości, aby wskazać, że właściwości są prywatne. Oddzielenie interfejsu od implementacji to świetny pomysł. Nazywa się to zwykle enkapsulacją.

Nieruchomości

Obiekt z nawiasami (...) nazywany jest literałem obiektowym. Możesz od razu umieścić niektóre właściwości w takich nawiasach (...). Na przykład łączy w pary „klucz: wartość i tak dalej”:

let user = ( // nazwa obiektu: "John", // kluczem "name" przechowuje wartość "John" age: 30 // by key "age" store value 30 }.!}

Właściwość ma klucz (znany również jako „nazwa” lub „identyfikator”) przed dwukropkiem „:” i wartość po prawej stronie. Obiekt użytkownika ma dwie właściwości. Wynikowy obiekt użytkownika JavaScript z dwoma podpisanymi plikami oznaczonymi „imię” i „wiek”. W każdej chwili możesz dodawać, usuwać i odczytywać z niego pliki. Dostęp do wartości właściwości uzyskuje się za pomocą notacji kropkowej. Może być dowolnego typu. Możesz dodać wartość logiczną. Aby usunąć właściwość, użyj opcji usuwania w przypadku błędu obiektu JavaScript.

Wszystkie obiekty błędów JavaScript są potomkami obiektu Error lub obiektu dziedziczonego:

  1. Obiekt Syntax Error dziedziczy z obiektu Error.
  2. Błąd analizy JSON określonego typu obiektu błędu składniowego.

Aby jeszcze głębiej zrozumieć, jak aplikacje radzą sobie z błędami JavaScript, przyjrzyj się bliżej Airbrake JavaScript, narzędziu do śledzenia błędów, które generuje alerty w czasie rzeczywistym i zapewnia natychmiastowy wgląd w to, co poszło nie tak z Twoim kodem JavaScript.

Komunikaty o błędach, które użytkownik może otrzymać przed usunięciem obiektu JavaScript:

  1. Zły znak kontrolny w literale ciągu.
  2. Zły znak w literale ciągu.
  3. Słabe wyjście Unicode.
  4. Zły znak ucieczki.
  5. Niezakończony ciąg.
  6. Nieoczekiwany kod inny niż numeryczny.
  7. Po przecinku dziesiętnym nie ma żadnych liczb.
  8. Niezakończona liczba ułamkowa.
  9. Po wskaźniku stopnia nie ma żadnych liczb.
  10. Po znaku wykładnika nie ma cyfr.
  11. Część wykładnicza nie ma liczby.
  12. Nieoczekiwany koniec danych.
  13. Nieoczekiwane słowo kluczowe.
  14. Nieoczekiwany symbol.
  15. Koniec danych przy czytaniu zawartości obiektu.
  16. Oczekiwana nazwa właściwości lub „)”.

Właściwości obliczeniowe

W literale obiektowym można używać nawiasów kwadratowych. Nazywa się je właściwościami obliczonymi. Przykład podano poniżej.

Znaczenie obliczonej właściwości jest proste: oznacza to, że nazwa właściwości musi pochodzić od owocu. Jeśli więc odwiedzający wpisze „jabłko”, torebką stanie się (jabłko: 5). Możesz użyć bardziej złożonych wyrażeń w nawiasach kwadratowych:

niech owoc = "jabłko";

: 5 // bag.appleComputers = 5

Nawiasy kwadratowe mają znacznie większą skuteczność niż zapis kropkowy. Pozwalają na nazwy właściwości i zmienne. Ale są też bardziej kłopotliwe w pisaniu. Dlatego w większości przypadków, gdy nazwy właściwości są znane i proste, używana jest kropka. A jeśli potrzebujesz czegoś bardziej złożonego, przejdź do nawiasów kwadratowych.

Rezerwacja słów

Zmienna nie może mieć nazwy równej jednemu ze słów zastrzeżonych, takich jak „for”, „let”, „return” itp. Jednak podczas sortowania obiektów JavaScript nie ma takiego ograniczenia.


W zasadzie dozwolona jest każda nazwa, ale jest jedna szczególna: „__proto__” jest traktowana szczególnie ze względów historycznych. Na przykład nie można ustawić wartości innej niż obiekt:

obj.__proto__ = 5;

alert(obj.__proto__); // nie działało zgodnie z przeznaczeniem

Jak widać z kodu, cel prymitywu 5 jest ignorowany. Może to być źródłem błędów, a nawet luk w zabezpieczeniach, jeśli operator zamierza przechowywać w obiekcie dowolne pary klucz-wartość i pozwolić odwiedzającemu określić klucze. W takim przypadku odwiedzający może wybrać „proto” jako klucz i dodać JavaScript do obiektu. Istnieje sposób, aby obiekty były traktowane za pomocą __proto__ jako zwykłej właściwości. Istnieje również inna mapa struktur danych obsługujących dowolne klucze.

Właściwości liczb całkowitych

Termin „właściwość całkowita” oznacza tutaj ciąg znaków, który można przekonwertować z liczby całkowitej bez modyfikacji. Na przykład „49” jest nazwą właściwości w postaci liczby całkowitej, ponieważ po przekonwertowaniu na liczbę całkowitą i z powrotem pozostaje taka sama. Ale „+49” i „1,2” takie nie są. Z drugiej strony, jeśli klucze nie są liczbami całkowitymi, wówczas są one wymienione w kolejności, w jakiej zostały utworzone. Przykład poniżej.


Aby rozwiązać problem z kodami wybierania, możesz „oszukiwać”, czyniąc kody niekompletnymi. Wystarczy dodać znak „+” (plus) przed każdym kodem. Teraz będzie działać zgodnie z przeznaczeniem.

Różnica między obiektami i prymitywami polega na tym, że są one przechowywane i kopiowane „przez odniesienie”. Wartości pierwotne są przypisywane i kopiowane „jako wartość całkowita”. Zmienna przechowuje „adres w pamięci”, a nie sam obiekt lub „odniesienie” do niego. Możesz użyć dowolnej zmiennej, aby uzyskać dostęp do jej zawartości i zmienić ją.


Powyższy przykład pokazuje, że jest tylko jeden obiekt i administrator, do którego można się zalogować. Następnie, jeśli później zostanie użyty inny klucz (użytkownik), użytkownik zauważy zmiany.

Operatory równości == i ścisła równość === dla obiektów działają w ten sam sposób. Dwa obiekty są równe tylko wtedy, gdy są tym samym obiektem. W przypadku porównań takich jak obj1 > obj2 lub porównań z prymitywem obj == 5, obiekty są konwertowane na prymitywy. Szczerze mówiąc, takie porównania są bardzo rzadko potrzebne i zwykle są wynikiem błędu w kodowaniu.

Walidacja obiektu JavaScript

Obiekty mają dostęp do dowolnej właściwości. Jeśli jednak w ogóle nie istnieje, nie będzie to błąd. Dostęp do nieistniejącej właściwości powoduje zwrócenie wartości niezdefiniowanej. Zapewnia bardzo powszechny sposób testowania właściwości i porównywania jej z niezdefiniowaną. Poniżej znajduje się przykład.


Używanie „in” dla właściwości, które przechowują niezdefiniowane. Zwykle ścisła kontrola porównania „=== niezdefiniowana” działa dobrze. Istnieje szczególny przypadek, w którym zawodzi, a „in” działa poprawnie. Dzieje się tak, gdy właściwość obiektu istnieje, ale pozostaje niezdefiniowana.


W powyższym kodzie właściwość obj.test technicznie istnieje. Dlatego operator in działa poprawnie. Takie sytuacje są bardzo rzadkie, ponieważ zwykle nie przypisuje się wartości undefiniowane. Najczęściej używane są wartości null „nieznane” lub „puste”. Zatem operator in jest w rzeczywistości gościem w kodzie.

Pętla „for..in”.

Aby przejść przez wszystkie klawisze od obiektu do obiektu, istnieje specjalna forma pętli: for..in. To zupełnie inna rzecz niż konstrukcja for(;;).

Poniżej znajduje się przykład.


Należy pamiętać, że wszystkie konstruktory „for” umożliwiają zadeklarowanie zmiennej pętli wewnątrz pętli jako klawisza let. Alternatywnie możesz zamiast tego użyć innej nazwy zmiennej, klucza.

Na przykład for(let prop in obj) jest również szeroko stosowane.

Istnieje alternatywny „nawias kwadratowy”, który działa z dowolnym ciągiem znaków.


Chodzi tu o to, aby klucze obiektu JavaScript były prawidłowym identyfikatorem zmiennej, co oznacza, że ​​nie ma w nich spacji ani innych ograniczeń. Należy zadbać o to, aby linia wewnątrz nawiasów została zacytowana prawidłowo. Nawiasy kwadratowe umożliwiają również uzyskanie nazwy właściwości z wyniku dowolnego wyrażenia, w przeciwieństwie do dosłownego ciągu znaków ze zmiennej:

niech key = "lubi ptaki";

// to samo co użytkownik["lubi ptaki"] = true;

użytkownik = prawda.

W tym przypadku zmienną kluczową można obliczyć w czasie wykonywania i zależy ona od danych wprowadzonych przez użytkownika, a następnie wykorzystać ją w celu uzyskania dostępu do właściwości. Daje to programistom większą elastyczność. Notacji kropkowej nie można używać w podobny sposób, ponieważ powodowałaby ona iterację po obiekcie JavaScript. Poniżej znajduje się przykład.


Obiekt stały

Zadeklarowany obiekt const można modyfikować. Przykład podano poniżej.


Może się wydawać, że obiekt JavaScript w linii (*) zgłosi błąd, ale tak nie jest. Dzieje się tak, ponieważ const przechwytuje wartość samego użytkownika. I tutaj użytkownik cały czas odwołuje się do tego samego obiektu. Linia (*) przechodzi do wnętrza obiektu, nie jest ponownie przypisana do użytkownika. Const zwróci błąd, jeśli spróbujesz ustawić użytkownika i coś innego. Klonowanie i łączenie Obiekt.przypisanie tworzy kolejne odniesienie do tego samego obiektu, jeśli zachodzi potrzeba jego zduplikowania. Jest to również wykonalne, ale trochę trudniejsze, ponieważ JavaScript nie ma wbudowanej metody. W rzeczywistości jest to rzadko konieczne. W większości przypadków stosuje się kopiowanie przez odniesienie. Ale jeśli naprawdę tego potrzebujesz, musisz utworzyć obiekt JavaScript i odtworzyć strukturę istniejącego, kopiując jego właściwości na prymitywnym poziomie. Poniżej znajduje się przykład.


Możesz także użyć do tego metody Object. przypisać. Argumenty dest i src1, ..., srcN są obiektami. Kopiuje właściwości wszystkich obiektów src1, ..., srcNINTO dest. Innymi słowy, właściwości wszystkich argumentów, począwszy od drugiego, są kopiowane do pierwszego. Następnie wraca do miejsca docelowego. Można go na przykład wykorzystać do połączenia kilku obiektów w jeden.


Możesz także użyć Object. przypisać, aby zastąpić prostą pętlę klonowania. Kopiuje wszystkie właściwości użytkownika do pustego obiektu i zwraca go, podobnie jak pętla, ale krócej. Do tej pory zakładano, że wszystkie właściwości użytkownika są prymitywne. Ale właściwości mogą być odniesieniami do innych obiektów.

Aby to naprawić, musisz użyć pętli klonowania, która sprawdza każdą wartość użytkownika i, jeśli jest to obiekt, replikuje jej strukturę. Nazywa się to „głębokim klonowaniem”.

Istnieje standardowy algorytm głębokiego klonowania, który obsługuje powyższy przypadek i bardziej złożone przypadki zwane algorytmem klonowania strukturalnego. Aby uniknąć wymyślania koła na nowo, możesz użyć działającej implementacji z biblioteki JavaScript lodash, metoda nazywa się _.cloneDeep(obj).

Zaawansowane metody

Jeśli programista wykona pętlę nad obiektem i chce uzyskać wszystkie właściwości w tej samej kolejności, w jakiej zostały dodane, może polegać na „porządku specjalnym”, w którym właściwości całkowite są sortowane, a inne tworzone są w kolejności, w jakiej obiekt JavaScript został utworzony .

Zaawansowane metody obiektowe zajmują się pojęciami rzadko używanymi w JavaScript. Dzieje się tak, ponieważ w normalnych scenariuszach te zaawansowane funkcje nie są potrzebne. Niektóre z tych metod mogą nie działać w starszych przeglądarkach, np. we wczesnych wersjach Netscape 4.

Prototyp można wykorzystać do tworzenia obiektów JavaScript i wszystkich metod mycircle, a nie tylko tych nowych. Ma to mieszany wpływ na wydajność. Nie powinny przechowywać poszczególne egzemplarze metody dla każdej instancji obiektu, więc może wymagać mniej pamięci do działania, ale przeglądarka musi szukać zakresu bieżącego i nadrzędnego, aby je znaleźć. Może to skutkować ekstremalnymi opóźnieniami. Ogólnie rzecz biorąc, użytkownik powinien używać tego, co jest odpowiednie dla kodu, a nie opierać swoją decyzję na wydajności, chyba że ma do czynienia z bardzo specyficznym kontrolowanym środowiskiem.


Zwróć prawdę

W niektórych przypadkach może być konieczne powiązanie właściwości obiektu z samym obiektem lub gdzieś w łańcuchu prototypów. W JavaScript wszystkie obiekty korzystają z metody hasOwnProperty, która zwraca wartość true, jeśli dana właściwość jest powiązana z pojedynczą instancją obiektu. W takim przypadku możliwe staje się sprawdzenie, czy konstruktor obiektu ma tę samą właściwość o tej samej wartości, co sama instancja obiektu. Może to dawać nieprawidłowe wyniki, jeśli istnieją oddzielne właściwości obiektu JavaScript o tej samej wartości zarówno dla instancji obiektu, jak i prototypu obwodu. Metoda hasOwnProperty przyjmuje pojedynczy parametr – nazwę właściwości w postaci ciągu znaków.


W podobny sposób możesz tworzyć metody prywatne. Jest to po prostu funkcja utworzona wewnątrz funkcji konstruktora. Niektórym może się to wydawać mylące, ale tak to działa. Funkcja prywatna może zostać wywołana tylko przez samego konstruktora lub metody zdefiniowane w linii. Można ich używać jako metod publicznych, jeśli są przypisane do publicznego konstruktora i można uzyskać do nich dostęp za pomocą publicznych metod obiektów JavaScript.

funkcja myob() ( funkcja cantBeSeen() ( alert(tajnaWartość);

) var sekretnawartość = "";

this.method1 = funkcja () ( secretValue = "bez niespodzianek";!}

this.method2 = cantBeSeen;

) var oneOb = nowy myob();

jednaOb.metoda1();

//ostrzega „bez niespodzianek” oneOb.method2();

//ostrzega „bez niespodzianek”.

Szablon polecenia

Obiekty poleceń pozwalają na luźno powiązane systemy, oddzielając te, które wysyłają żądanie od obiektów, i te, które faktycznie przetwarzają żądanie. Żądania te nazywane są zdarzeniami, a kod przetwarzający żądania nazywany jest procedurami obsługi zdarzeń.

Załóżmy, że tworzysz aplikacje obsługujące akcje schowka Wytnij, Kopiuj i Wklej. Akcje te można wywołać w aplikacji na różne sposoby: poprzez system menu, menu kontekstowe, na przykład kliknięcie pola tekstowego prawym przyciskiem myszy, lub skrót klawiaturowy. Obiekty poleceń umożliwiają centralizację przetwarzania tych akcji, po jednej dla każdej operacji, gdy do przetworzenia wszystkich żądań Wytnij, jedno do wszystkich żądań Kopiowania i jedno do wszystkich żądań Wklej, potrzebne jest tylko jedno polecenie.

Ponieważ zespoły centralizują całe przetwarzanie, często zajmują się także obsługą funkcji cofania dla całej aplikacji. Znaczące ulepszenia można osiągnąć stosując nowoczesne techniki JavaScript, co skutkuje bardziej wydajnymi, niezawodnymi i łatwiejszymi w utrzymaniu aplikacjami.

Aby dowiedzieć się jak to zrobić, możesz skorzystać z szablonów JavaScript + jQuery. Ten unikalny pakiet zawiera zoptymalizowany JavaScript dla wszystkich szablonów GoF wykorzystujących bardziej zaawansowane funkcje, takie jak przestrzenie nazw, prototypy, moduły, obiekty funkcyjne, zamknięcia, funkcje anonimowe i inne. Jeśli użytkownicy potrzebują najnowszych narzędzi i technik dotyczących szablonów JavaScript, szablonów jQuery i architektur szablonów, jest to najlepszy przypadek użycia. Pakiet ten zawiera cenne i aktualne informacje dla programistów JavaScript. Oto zawartość zestawu:

  1. Szablony GoF zoptymalizowane pod kątem JavaScript.
  2. Nowoczesne wzorce projektowe JavaScript.
  3. Wzorce projektowe widoku modelu.
  4. Szablony projektów jQuery.
  5. Wzorce architektoniczne idiomów JavaScript.
  6. Przykładowe aplikacje (MVC, SPA, itp.)

Zaproponowane podstawy składni obiektów JavaScript są bardzo ważne dla początkujących programistów. Najpierw trzeba rozumieć obiekty, potem będzie wiedza z programowania obiektowego. Głębokie zrozumienie tego materiału jest niezwykle istotne, ponieważ stanowi on podstawę dla reszty języka JavaScript.

W tym artykule chcę w możliwie najpełniejszy i spójny sposób opowiedzieć o tym, czym jest obiekt w JavaScript, jakie są jego możliwości, jakie relacje można budować między obiektami i jakie z tego wynikają metody „natywnego” dziedziczenia, jak to wszystko wpływa wydajność i w ogóle co z tym wszystkim zrobić :)

W artykule NIE będzie ani słowa o: emulacji tradycyjnego paradygmatu klasy-obiektu, cukrze składniowym, opakowaniach i frameworkach.

Złożoność materiału będzie rosła od początku do końca artykułu, więc dla profesjonalistów pierwsze części mogą wydawać się nudne i banalne, ale wtedy będzie znacznie ciekawiej :)

Obiekty w JavaScript

Wiele artykułów zawiera frazę „W JavaScript wszystko jest obiektem”. Technicznie nie jest to do końca prawdą, ale na początkujących robi dobre wrażenie :)

Rzeczywiście, większość języka jest przedmiotem, a nawet to, co nie jest przedmiotem, może mieć pewne swoje możliwości.

Ważne jest, aby zrozumieć, że słowo „obiekt” nie jest tu użyte w znaczeniu „przedmiot jakiejś klasy”. Obiekt w JavaScript to przede wszystkim zbiór właściwości (jeśli wolisz, możesz go nazwać tablicą lub listą asocjacyjną) składający się z par klucz-wartość. Co więcej, kluczem może być tylko ciąg znaków (nawet dla elementów tablicy), ale wartością może być dowolny typ danych wymienionych poniżej.

Zatem w JavaScript jest 6 podstawowe typy dane to Niezdefiniowane (wskazujące na brak wartości), Null, Boolean (Boolean), String (string), Number (liczba) i Object (obiekt).
Co więcej, pierwszych 5 jest prymitywny typy danych, ale obiekt nie. Dodatkowo możemy umownie przyjąć, że typ Object posiada „podtypy”: tablica (Array), funkcja (Function), Wyrażenie regularne(RegExp) i inne.
Jest to opis nieco uproszczony, ale w praktyce zazwyczaj wystarczający.

Ponadto typy pierwotne String, Number i Boolean są w pewien sposób powiązane z nieprymitywnymi „podtypami” obiektu: odpowiednio String, Number i Boolean.
Oznacza to, że na przykład ciąg znaków „Hello, world” można utworzyć albo jako wartość pierwotną, albo jako obiekt typu String.
Krótko mówiąc, robi się to po to, aby programista mógł używać metod i właściwości podczas pracy z prymitywnymi wartościami tak, jakby były obiektami. Więcej na ten temat możesz przeczytać w odpowiedniej części tego artykułu.

Praca z linku

Referencja to sposób dostępu do obiektu pod różnymi nazwami. Praca z dowolnymi obiektami odbywa się wyłącznie na podstawie odniesienia.
Pokażmy to na przykładzie:
test= funkcja () (alert("Witam!" )) //Utwórz funkcję (alert("Hello!")) (a funkcja, jak pamiętamy, jest pełnoprawnym obiektem) i uczyń zmienną testową referencją do niej
test_link=test; //test_link odnosi się teraz również do naszej funkcji
test(); //Cześć!
test_link(); //Cześć!


Jak widzimy, zarówno pierwszy link, jak i drugi dają ten sam wynik.
Musimy zdać sobie sprawę, że nie mamy żadnej funkcji o nazwie test i że zmienna testowa nie jest jakimś łączem „głównym” lub „podstawowym”, a „link_testowy” jest łączem pobocznym.

Nasza funkcja, jak każdy inny obiekt, jest po prostu obszarem w pamięci i wszystkie odniesienia do tego obszaru są absolutnie równoważne. Co więcej, obiekt może w ogóle nie mieć żadnych referencji - w tym przypadku nazywa się go anonimowym i można go użyć dopiero od razu po utworzeniu (np. przekazać do funkcji), w przeciwnym razie dostęp do niego będzie niemożliwy i wkrótce zostać zniszczone przez moduł zbierający śmieci (odśmiecanie), który jest odpowiedzialny za usuwanie obiektów bez referencji.

Zobaczmy, dlaczego zrozumienie tego jest tak ważne:

test=(propozycja: "jakiś tekst" ) //Utwórz obiekt z właściwością prop
test_link=test; //Utwórz kolejne łącze do tego obiektu

Alert(test.prop); //jakiś tekst

//Zmień właściwość obiektu
test_link.prop="nowytekst" ;

Alert(test.prop); //nowytekst
alert(test_link.prop); //nowytekst
/*Można powiedzieć, że nieruchomość zmieniła się tu i tam - ale to nieprawda.
Jest tylko jeden obiekt. Zatem właściwość zmieniła się raz, a łącza nadal wskazują tam, gdzie wskazują. */

//Dodaj nową właściwość i usuń starą
test.new_prop="witaj" ;
usuń test.prop;

Alert(test_link.prop); //nieokreślony — ta właściwość już nie istnieje
alert(test_link.new_prop);

//Usuń link
usuń test;
alert(test.nowy_prop);
/*W tym momencie skrypt zgłosi błąd, ponieważ test już nie istnieje, a test.new_prop tym bardziej nie istnieje */
alert(test_link.new_prop); //Witam
/* ale tutaj wszystko jest w porządku, ponieważ nie usunęliśmy samego obiektu, a jedynie link do niego. Teraz na nasz obiekt wskazuje pojedynczy link, test_link */

//Utwórz nowy obiekt
test=link_testowy; //Najpierw utwórz ponownie łącze testowe
test_link=(prop: "jakiś tekst" ) //A oto nowy obiekt

Alert(test_link.prop); //jakiś tekst
alert(test.prop); //nieokreślony
/* Utworzenie nowego obiektu przerywa łącze referencyjne i teraz testy i test_link wskazują na różne obiekty.
W rzeczywistości jest to równoznaczne z usunięciem linku testowego i utworzeniem go ponownie, ale ze wskazaniem innego obiektu */
alert(test.nowy_prop); //witaj - teraz test zawiera link do naszego pierwszego obiektu


* Ten kod źródłowy został wyróżniony za pomocą narzędzia Source Code Highlighter.

Takie zachowanie obiektów często rodzi wiele pytań początkującym programistom, dlatego mam nadzieję, że ten tekst wniesie pewną jasność. Jeśli chcemy stworzyć naprawdę nową, niezależną kopię obiektu, a nie łącze, to jedynym sposobem, aby to zrobić, jest utworzenie nowego obiektu i skopiowanie tam wymaganych właściwości.

Warto też dodać, że praca z obiektami przez referencje, poza wymienionymi powyżej zabawnymi efektami, zapewnia także znaczną oszczędność pamięci, co jest istotne przy powszechne stosowanie jeden obiekt w różnych miejscach programu.

Prymitywne wartości

Jak wspomniałem powyżej, typy danych String i Number mogą być obiektami lub wartościami pierwotnymi.
obj= nowy String("cześć" ); //Utwórz ciąg znaków jako obiekt
proste="cześć" ; //Utwórz wartość pierwotną

Alert(obiekt); //Witam
alert (prosty); //cześć - na razie wszystko jest przewidywalne

Alert(długość obiektu); //6 – obiekt typu String posiada właściwość długości, która przechowuje długość łańcucha
alert(prosta.długość); //6
/* Chociaż simple nie jest obiektem, możemy uzyskać dostęp do tego samego zestawu właściwości, co obiekt String. To całkiem wygodne */

Obj.prop="tekst" ;
simple.prop="tekst" ;

Alert(obj.prop); //text - ponieważ obj jest zwykłym obiektem, możemy łatwo nadać mu inną właściwość
alert(simple.prop); //nieokreślony - ale prosty nie jest obiektem i ta liczba u nas nie będzie działać

* Ten kod źródłowy został wyróżniony za pomocą narzędzia Source Code Highlighter.


To samo dotyczy typów Number i Boolean (no cóż, z tą różnicą, że nie mają one właściwości długości, ale mają wiele innych świetnych właściwości).
Używanie ciągów i liczb jako obiektów nie ma żadnych praktycznych korzyści, ponieważ prymitywne wartości są wygodniejsze w użyciu, ale jednocześnie zachowują całą niezbędną funkcjonalność. Aby jednak dopełnić obrazu, konieczne jest zrozumienie tego mechanizmu.

Nie mylmy stosowania wartości prymitywnych z użyciem literałów – np. niezależnie od tego, czy utworzymy tablicę jako „test=new Array()”, czy jako „test=”, wynikiem nadal będzie ten sam obiekt. Nie otrzymamy żadnych wartości pierwotnych.

Tworzenie i używanie obiektów

Zatem w przeciwieństwie do języków, które implementują paradygmat obiektu klasy, nie musimy najpierw tworzyć klasy, a następnie tworzyć jej obiekt. Możemy od razu utworzyć obiekt, co zrobimy w poniższym przykładzie:
test=(
simple_property: „Witam”,
właściwość_obiektu: (
użytkownik_1: "Petya" ,
użytkownik_2: „Wasja”
},
funkcja_właściwość: funkcja (użytkownik) (
alert(ta .simple_property + ", " + ta .object_property);
}
}

Test.funkcja_właściwość("użytkownik_1" ); //Witam, Petya.

* Ten kod źródłowy został wyróżniony za pomocą narzędzia Source Code Highlighter.


Mamy obiekt testowy, który ma 3 właściwości, których nazwy, mam nadzieję, mówią same za siebie. To, co nas najbardziej interesuje, to właściwość_funkcji, która zawiera tę funkcję. Funkcję taką można nazwać metodą obiektową.

Nasza funkcja dwukrotnie używa słowa kluczowego this, które jest wskaźnikiem (czyli referencją) do obiektu, z którego wywoływana jest funkcja. Zatem this.simple_property=test.simple_property="Witam" i this.object_property=test.object_property="Peter".

Ważne jest, aby było jasne, że zawsze wskazuje to na obiekt, z którego funkcja jest wywoływana, a nie na obiekt, do którego ona należy. Chociaż w w tym przykładzie to jest ten sam obiekt, nie zawsze tak jest.

test.function_property("użytkownik_1" ); //Witam, Petya.

Test2=nowy obiekt(); //Inna forma tworzenia nowego obiektu, podobna do test2=()

Test.function_property.call(test2, "użytkownik_1" ); //błąd
/* Metoda wywołania umożliwia wywołanie funkcji w imieniu innego obiektu. W tym przypadku wywołujemy metodęfunction_property obiektu testowego, która nie wskazuje już na obiekt testowy, ale na obiekt test2. I ponieważ nie ma właściwości object_property, wtedy przy próbie uzyskania this.object_property skrypt zgłosi błąd */

//spróbujmy naprawić sytuację
test2.simple_property="Dzień dobry" ;
test2.object_property=test.object_property; //W tym przypadku użyjemy określenia obiektu przez referencję, aby nie powielać kodu

Test.function_property.call(test2, "użytkownik_1" ); //Dzień dobry, Petya.


* Ten kod źródłowy został wyróżniony za pomocą narzędzia Source Code Highlighter.

Z przykładu powinno być również jasne, że nie ma jasnych kroków tworzenia i używania obiektu. Obiekt można modyfikować w dowolny sposób w dowolnym momencie – przed, po, a nawet w trakcie użytkowania. To także istotna różnica w stosunku do „tradycyjnego” OOP.

Konstruktor

W powyższym przykładzie utworzyliśmy 2 obiekty, które miały pewne podobieństwo. Obie miały właściwości simple_property i object_property. Oczywiście podczas pisania prawdziwego kodu często pojawia się zadanie stworzenia identycznych lub po prostu podobnych obiektów. I oczywiście nie musimy tworzyć każdego takiego obiektu ręcznie.

Z pomocą przyjdzie nam projektant. Konstruktor w JavaScript nie jest częścią klasy (ponieważ nie ma klas), ale po prostu funkcją samą w sobie. Najczęstsza funkcja.

make_me= funkcja (_nazwa) (
alert("Zostałem uruchomiony" );
to .name=_name;

}


/* Zastanówmy się, co się tutaj dzieje. Interpreter widzi nowy operator i sprawdza, co jest po jego prawej stronie. Ponieważ make_me jest funkcją i można jej używać jako konstruktora, następnie w pamięci tworzony jest nowy obiekt i uruchamiana jest funkcja make_me, która wskazuje dokładnie na ten nowy obiekt. Następnie do obiektu dodawany jest obiekt z właściwością name, do której przypisuje się wartość z argumentu _name oraz metodą show_name. Poza tym (nie wiem w którym momencie, ale to nie ma znaczenia) zmienna potomna zaczyna wskazywać na nasz nowy, dopiero co narodzony obiekt */

Alert(imię.dziecka); //Wasja
dziecko.show_name(); //Wasja


dziecko2.show_name(); //Piotr

Child2.show_name=funkcja () (alert( „Nie powiem jak się nazywam”);} //Pamiętaj, że w każdej chwili możemy zmienić nasze obiekty
dziecko2.show_name(); //Nie powiem jak się nazywam

Dziecko.show_name(); //Vasya - dzieci nie wpływają na siebie w żaden sposób


* Ten kod źródłowy został wyróżniony za pomocą narzędzia Source Code Highlighter.

Projektanta można też porównać do ojca – rodzi dziecko, nadając mu pewne cechy, ale zaraz po stworzeniu dziecko staje się całkowicie niezależne od rodzica i może bardzo różnić się od swoich braci.
Jeśli przypomnimy sobie opis typów danych na początku artykułu, stanie się jasne, że Obiekt i jego podtypy (Funkcja, Tablica i inne) są w rzeczywistości konstruktorami, które nadają tworzonemu obiektowi możliwości funkcji, tablicy itp.

Zatem jest już znacznie lepiej. Mamy teraz możliwość tworzenia obiektów według jakiegoś wzoru. Jednak nie wszystko jest jeszcze w porządku. Po pierwsze, każdy obiekt, który tworzymy, oraz wszystkie jego właściwości i metody zajmują osobne miejsce w pamięci, choć pod wieloma względami się powtarzają. Po drugie, co jeśli chcemy zachować połączenie pomiędzy rodzicem a dzieckiem i mieć możliwość jednoczesnej zmiany wszystkich obiektów podrzędnych. Z pomocą przyjdzie nam prototyp.

Prototyp

Tak jak każde dziecko ma ojca i matkę (przynajmniej w sensie biologicznym), tak samo jest z każdym obiektem w JavaScript. A jeśli ojciec, jak ustaliliśmy, pracuje jako projektant, to matka jest tylko prototypem. Zobaczmy, jak to się dzieje:
make_me= funkcja (_nazwa) (
alert("Zostałem uruchomiony" );
to .name=_name;
this .show_name=function () (alert(this .name);)
}
/*
Widząc słowo kluczowe funkcji, interpreter sprawdza kod po jego prawej stronie i tak dalej. wszystko jest w porządku - tworzy nowy obiekt w pamięci, co jest również naszą funkcją. Następnie automatycznie (bez ingerencji programisty) tworzona jest właściwość prototypowa tej funkcji, która odnosi się do pustego obiektu. Gdybyśmy zrobili to ręcznie, wyglądałoby to tak: make_me.prototype=new Object();

Następnie obiekt ten (wskazywany przez właściwość prototype) również automatycznie otrzymuje właściwość konstruktora, wskazującą z powrotem na funkcję. Okazuje się, że jest to łącze cykliczne.

Teraz ten obiekt, który można opisać jako (konstruktor: ...tutaj jest odniesienie do funkcji...) jest prototypem funkcji.
*/

//Obiekt - rzeczywiście obiekt
alert(typ make_me.prototype.constructor); //Funkcja jest naszą funkcją
alert(make_me.prototype.constructor === make_me); //PRAWDA

//Dodaj funkcje make_me do prototypu nowa metoda

Dziecko=nowy make_me("Wasja" ); //Zostałem uruchomiony
/* Teraz oprócz wszystkiego, co opisano w poprzednim przykładzie, w obiekcie podrzędnym tworzona jest dodatkowa ukryta właściwość [], która wskazuje na ten sam obiekt co make_me.prototype. Ponieważ Nieruchomość jest ukryta, nie możemy zobaczyć jej wartości ani jej zmienić - odgrywa jednak ważną rolę w dalszej pracy */

Alert(imię.dziecka); //Wasja
dziecko.show_name(); //Wasja

Child.set_name("Kolya" );
/* Najpierw interpreter szuka metody set_name w obiekcie podrzędnym. Ponieważ jej tam nie ma, kontynuuje wyszukiwanie we właściwości child.[, znajduje ją tam i uruchamia. */
dziecko.show_name(); //Kolia - teraz Wasia ma na imię Kola :)

Make_me.prototype.show_name2=funkcja () (alert("Witam, " + to .name;) //Ponieważ prototyp to zwykły przedmiot, równie dobrze możemy go zmieniać na bieżąco

Dziecko2=nowy make_me("Petya" );
dziecko2.show_name2(); //Witam, Petya
dziecko.show_name2(); //Cześć, Kola - zmiany w prototypie dotyczą nie tylko nowo tworzonych obiektów, ale także wszystkich starych

Child2.show_name2=funkcja () (alert( „Nie powiem jak się nazywam”);} //Nadal możemy zmienić sam obiekt, a nowa metoda show_name2 w tym obiekcie (i tylko w nim) będzie niejako „nadpisywać” starą metodę z prototypu
dziecko2.show_name2(); //Nie powiem jak się nazywam - bo... mamy teraz własną metodę show_name2, następnie jest ona wywoływana i wyszukiwanie w prototypie nie następuje

Dziecko.show_name2(); //Witaj, Kola - tutaj wszystko jest nadal takie samo

Make_me.prototype=(prop: "cześć" ) //Spróbujmy ponownie odtworzyć prototyp

Alert(dziecko.prop); //nieokreślony
dziecko.show_name2(); //Cześć Kola
/* Jeśli pamiętasz, na czym polega praca przez referencje, wszystko jest jasne. Odtworzenie prototypu zrywa połączenie i teraz właściwość [] obiektów child i child2 wskazuje na jeden obiekt (który wcześniej był prototypem funkcji make_me), a właściwość make_me.prototype wskazuje na inny obiekt, który jest nowym prototyp funkcji make_me */

Dziecko3=nowy make_me("Oleg" );
alert(dziecko3.prop); //cześć - zgodnie z oczekiwaniami


* Ten kod źródłowy został wyróżniony za pomocą narzędzia Source Code Highlighter.

Jak widać na przykładzie, dopóki ojciec pozostaje wierny matce (czyli dopóki rodzaj funkcji pozostaje ten sam), wszystkie dzieci są zależne od matki i wrażliwe na wszelkie zachodzące w niej zmiany. Jednak gdy tylko rodzice się rozwodzą (projektant zmienia prototyp na inny), dzieci natychmiast uciekają na wszystkie strony i nie ma już z nimi kontaktu.

Trochę o terminologii
Dopóki pierwotne połączenie pomiędzy projektantem a prototypem nie zostanie zerwane, możemy zaobserwować następujący obraz:

make_me= funkcja (_nazwa) (
alert("Zostałem uruchomiony" );
to .name=_name;
this .show_name=function () (alert(this .name);)
}

Make_me.prototype.set_name=funkcja (_nazwa) (to .name=_name;)
dziecko=nowy make_me("Wasja" );

Alert(typ make_me.prototype); //obiekt - funkcja ma właściwość prototyp
alert(typ dziecka.prototyp); //nieokreślony - utworzony obiekt NIE posiada właściwości prototype
alert(child.constructor.prototype === make_me.prototype); //true - ale obiekt ma właściwość konstruktora, która wskazuje na funkcję konstruktora make_me, która z kolei ma właściwość prototype


* Ten kod źródłowy został wyróżniony za pomocą narzędzia Source Code Highlighter.

Jak zauważyłem po przeczytaniu licznych forów na ten temat, głównym problemem ludzi jest mylenie właściwości prototypu funkcji z ukrytą właściwością [] obiektu utworzonego przez tę funkcję.
Obie te właściwości są odniesieniem do tego samego obiektu (o ile nie zostanie zerwane pierwotne połączenie między prototypem a konstruktorem), niemniej jednak są to różne właściwości, o różnych nazwach, z których jedna jest dostępna dla programisty i drugi nie.

Zawsze trzeba jasno zrozumieć, że jeśli mówimy o prototypie konstruktora, to zawsze jest to właściwość prototypu, a jeśli mówimy o prototypie tworzonego obiektu, to jest to właściwość ukryta [].

Dziedzictwo

Teraz wiemy, że każdy obiekt ma ukryte odniesienie do prototypu i każdy prototyp jest zwykłym obiektem.
Najbardziej wrażliwi czytelnicy już wyczuli zapach rekurencji :)
Rzeczywiście, ponieważ prototyp jest zwykłym obiektem, to on z kolei ma powiązanie ze swoim prototypem i tak dalej. W ten sposób implementowana jest hierarchia prototypów.
ptak= funkcjonować()() //To jest konstruktor ptaka
ptak.prototype.cry=funkcja ()(alert("Płacz!");) //Ptak może krzyczeć
bird.prototype.fly=function ()(alert("Lecę!");) //i lecę

Kaczka=funkcja () ()
kaczka.prototype=nowy ptak();
duck.prototype.cry=funkcja ()(alert("Kwak-kwak!" ;) //Kaczka krzyczy inaczej
kaczka.prototype.constructor=kaczka; //Wymuś ustawienie właściwości prototype.constructor na duck, ponieważ w przeciwnym razie będzie odnosić się do ptaka

Billy = nowa kaczka(); //Billy jest naszą kaczką
billy.fly(); //Latam! - Billy potrafi latać, bo jest ptakiem.
billy.cry(); //Kwak Kwak! - Billy krzyczy kwak-kwak, bo jest kaczką.


* Ten kod źródłowy został wyróżniony za pomocą narzędzia Source Code Highlighter.

W ten sposób możesz zaimplementować hierarchię na dowolnym poziomie zagnieżdżenia.

Zadanie z gwiazdką

Skoro już tak dużo o tym wszystkim wiemy, spróbujmy dowiedzieć się, ile dzieje się w tych trzech linijkach
make_me= funkcjonować()()
dziecko=nowy make_me();
alert(dziecko.toString()); //wyjścia

* Ten kod źródłowy został wyróżniony za pomocą narzędzia Source Code Highlighter.

W pierwszej linii tworzymy nową funkcję i zmienną o nazwie make_me, która wskazuje na tę funkcję. Spowoduje to utworzenie prototypu funkcji make_me.prototype, który zawiera właściwość konstruktora wskazującą na make_me.
Ale to nie wszystko:)
Ponieważ funkcja make_me jest również obiektem, to ona z kolei ma ojca i matkę, tj. projektant i prototyp. Jego konstruktor jest natywną funkcją języka Function(), a jego prototypem jest obiekt zawierający metody call, Apply itp. - To dzięki temu prototypowi możemy zastosować te metody w dowolnej funkcji. Zatem funkcja make_me ma właściwość [] wskazującą na Function.prototype.

Z kolei prototyp konstruktora Function to także obiekt, którego konstruktorem jest (niespodzianka!) Object (czyli Function.prototype.[].constructor===Object), a prototyp to obiekt zawierający standardowe właściwości i metody obiektu takie jak toString, hasOwnProperty i inne (innymi słowy - Function.prototype.[]["hasOwnProperty"] - to jest dokładnie metoda, którą możemy zastosować we wszystkich obiektach pochodnych - i jest to metoda własna tego obiektu, a nie odziedziczonego). W ten interesujący sposób odkrywamy, że wszelkiego rodzaju obiekty wywodzą się z obiektu.

Czy możemy kontynuować dalej? Okazuje się, że nie. Obiekt.prototyp zawiera podstawowe właściwości obiektu właśnie dlatego, że nie posiada własnego prototypu. Obiekt.prototyp.[]=null; W tym momencie kończy się podróż przez łańcuch prototypów w poszukiwaniu właściwości lub metody.

Inny interesujący fakt- konstruktorem obiektu jest funkcja. Te. Obiekt.[].constructor===Funkcja.
Istnieje jeszcze jedno odniesienie cykliczne - konstruktorem Object jest Function, a konstruktorem Function.prototype jest Object.

Wróćmy do naszego przykładu. Zrozumieliśmy już, jak tworzona jest funkcja, teraz przejdźmy do drugiej linii. Tworzymy tam obiekt potomny, którego konstruktorem jest funkcja make_me i którego prototypem jest make_me.prototype.

Cóż, w trzeciej linii widzimy, jak interpreter przechodzi w górę łańcucha, od dziecka do dziecka.[] (aka make_me.prototype), następnie do dziecka.[].[] (aka Object.prototype) i już tam znajduje metodę toString, która uruchamia wykonanie.

Zanieczyszczenia

Może się wydawać, że dziedziczenie poprzez prototypy jest jedyną możliwą metodą w JavaScript. To jest źle.
Mamy do czynienia z bardzo elastycznym językiem, który daje możliwości, a nie zasady.

Przykładowo, jeśli chcemy, nie możemy w ogóle używać prototypów, ale programować wykorzystując koncepcję miksów. Do tego będziemy potrzebować naszych starych dobrych przyjaciół - projektantów.

//To jest ludzki konstruktor
człowiek=funkcja() (
this .live=function ()(alert("Jestem na żywo" ;) //Człowiek wie jak żyć
this .walk=function ()(alert("Idę" ;) //Mężczyzna może chodzić
}

//To jest konstruktor poety
poeta=funkcja ()(
to .kill=funkcja ()(alert( „Poeta zabił człowieka”);} //Poeta może zabić człowieka
to .live=function ()(alert("Nie żyję" ;) //Człowiek umrze z tego powodu
}

Władimir=nowy człowiek(); //Władimir jest mężczyzną
władimir.live(); //Ja żyję - on żyje
Władimir.walk(); //Ja idę - on idzie

Poeta.call(vladimir); //Uruchom konstruktora poet dla obiektu vladimir
wladimir.kill(); //Poeta zabił człowieka
władimir.live(); //Nie żyję

//Teraz skup się
mężczyzna.zadzwoń(władimir);
władimir.live(); //Żyję


* Ten kod źródłowy został wyróżniony za pomocą narzędzia Source Code Highlighter.

Co widzimy w tym przykładzie? Po pierwsze, możliwe jest dziedziczenie z kilku obiektów, które nie znajdują się w tej samej hierarchii. W przykładzie są ich 2, ale może być ich tyle, ile chcesz.
Po drugie, w ogóle nie ma hierarchii. O przesłanianiu właściwości i metod decyduje wyłącznie kolejność wywoływania konstruktorów.
Po trzecie, jest to możliwość jeszcze bardziej dynamicznej zmiany obiektu, konkretnie pojedynczego obiektu, a nie wszystkich jego potomków, jak przy zmianie prototypu.

Aktualizacja: Zamknięcia i nieruchomości prywatne

Aby nie zanudzać tym i tak już dość obszernym artykułem, podaję link do wpisu Zamknięcia w JavaScript, gdzie jest to napisane dość szczegółowo.

Co teraz z tym wszystkim zrobić

Jak powiedziałem powyżej, dowolna modyfikacja poszczególnych obiektów, użycie konstruktorów, miksin i elastyczność prototypów to tylko narzędzia, możliwości, które pozwalają programiście stworzyć kod zarówno straszny, jak i wspaniały pod każdym względem. Ważne jest tylko, aby zrozumieć, jakie problemy rozwiązujemy, jakimi środkami, jakie cele osiągamy i jaką cenę za to płacimy.

Co więcej, kwestia ceny jest dość nietrywialna, zwłaszcza jeśli mówimy o rozwoju przeglądarki Internet Explorera Wersje 6 i 7.
1. Pamięć - tutaj wszystko jest proste. We wszystkich przeglądarkach dziedziczenie na prototypach zajmuje znacznie mniej pamięci niż przy tworzeniu metod poprzez konstruktory. Co więcej, im więcej mamy metod i właściwości, tym większa różnica. Warto jednak pamiętać, że jeśli nie będziemy mieli tysiąca identycznych obiektów, a tylko jeden, to i tak zużycie pamięci będzie niewielkie, bo Należy tu wziąć pod uwagę inne czynniki.
2. Czas procesora - tutaj główne subtelności dotyczą konkretnie przeglądarek firmy Microsoft.
Z jednej strony obiekty, których metody i właściwości tworzone są za pomocą konstruktora, można tworzyć wielokrotnie (w niektórych przypadkach dziesiątki i setki razy) wolniej niż za pomocą prototypu. Im więcej metod, tym wolniej. Jeśli więc Twój IE zawiesza się na kilka sekund podczas inicjalizacji skryptu, istnieje powód, aby szukać w tym kierunku.

Z drugiej strony, własne metody obiekty (utworzone za pomocą konstruktora) mogą działać nieco szybciej niż obiekty prototypowe. Jeśli desperacko potrzebujesz przyspieszyć wykonanie określonej metody w tej przeglądarce, musisz wziąć to pod uwagę. Należy pamiętać, że przyspieszane jest wywołanie metody (czyli jej wyszukiwanie w obiekcie), a nie jej wykonanie. Jeśli więc sama metoda będzie działać przez sekundę, nie zauważysz znacznego wzrostu wydajności.

W innych przeglądarkach podobne problemy Zaobserwowano, że czas tworzenia obiektów i wywoływania ich metod jest w przybliżeniu taki sam w obu podejściach.

P.S. Zazwyczaj w artykułach tego typu autor oferuje jakiś rodzaj opakowania, albo próbując zaimplementować dziedziczenie obiektów klasowych w oparciu o dziedziczenie prototypowe, albo po prostu cukier syntaktyczny dla dziedziczenia prototypowego. Nie robię tego celowo, bo... Wierzę, że osoba, która rozumie sens tego artykułu, jest w stanie napisać dla siebie dowolne opakowanie i wiele innych ciekawych rzeczy :)

Tagi: Dodaj tagi