Załóżmy, że mam obiekt foo
w swoim kodzie JavaScript. foo
jest złożonym obiektem i jest generowany gdzie indziej. Jak mogę zmienić prototyp foo
obiektu?
Moją motywacją jest ustawienie odpowiednich prototypów obiektów serializowanych z .NET do literałów JavaScript.
Załóżmy, że napisałem następujący kod JavaScript na stronie ASP.NET.
var foo = <%=MyData %>;
Załóżmy, że MyData
jest to wynikiem wywołania .NET JavaScriptSerializer
na Dictionary<string,string>
obiekcie.
W czasie wykonywania wygląda to następująco:
var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}];
Jak widać, foo
staje się tablicą obiektów. Chciałbym mieć możliwość zainicjowania foo
odpowiedniego prototypu. Ja nie chce modyfikować Object.prototype
ani Array.prototype
. W jaki sposób mogę to zrobić?
javascript
function-prototypes
Rzeka Vivian
źródło
źródło
extend
lub Googlegoog.inherit
? Wielu programistów zapewnia sposoby budowania dziedziczenia przed wywołaniem starszegonew
konstruktora - czyli zanim otrzymaliśmyObject.create
i nie musieli martwić się o nadpisanieObject.prototype
.Odpowiedzi:
EDYCJA Luty 2012: poniższa odpowiedź nie jest już aktualna. __proto__ jest dodawany do ECMAScript 6 jako „normatywny opcjonalny”, co oznacza, że nie jest wymagany do zaimplementowania, ale jeśli jest, musi być zgodny z podanym zestawem reguł. Obecnie jest to nierozwiązane, ale przynajmniej będzie oficjalnie częścią specyfikacji JavaScript.
To pytanie jest o wiele bardziej skomplikowane, niż się wydaje na pierwszy rzut oka, i wykracza poza poziom płac większości ludzi, jeśli chodzi o znajomość wewnętrznych elementów Javascript.
prototype
Właściwość obiektu jest używany przy tworzeniu nowych obiektów podrzędnych tego obiektu. Zmiana nie odbija się w samym obiekcie, a raczej jest odzwierciedlana, gdy ten obiekt jest używany jako konstruktor dla innych obiektów i nie ma pożytku ze zmiany prototypu istniejącego obiektu.Obiekty mają wewnętrzną właściwość [[prototyp]], która wskazuje na bieżący prototyp. Działa to tak, że za każdym razem, gdy wywoływana jest właściwość obiektu, zaczyna się ona od obiektu, a następnie przechodzi w górę przez łańcuch [[prototyp]], aż znajdzie dopasowanie lub niepowodzenie po głównym prototypie obiektu. W ten sposób Javascript umożliwia budowanie i modyfikowanie obiektów w środowisku wykonawczym; ma plan poszukiwania tego, czego potrzebuje.
Ta
__proto__
właściwość istnieje w niektórych implementacjach (obecnie dużo): we wszystkich implementacjach Mozilli, we wszystkich webkitach, które znam, w innych. Ta właściwość wskazuje na wewnętrzną właściwość [[prototyp]] i umożliwia modyfikację obiektów po utworzeniu. Wszelkie właściwości i funkcje zostaną natychmiast przełączone w celu dopasowania do prototypu dzięki wyszukiwaniu łańcuchowemu.Ta funkcja, choć jest obecnie standaryzowana, nadal nie jest wymaganą częścią JavaScript, aw językach obsługujących ją istnieje duże prawdopodobieństwo, że kod zostanie umieszczony w kategorii „niezoptymalizowana”. Silniki JS muszą zrobić wszystko, co w ich mocy, aby sklasyfikować kod, szczególnie ten „gorący”, do którego dostęp jest bardzo często, a jeśli robisz coś wymyślnego, na przykład modyfikowanie
__proto__
, w ogóle nie zoptymalizują Twojego kodu.Ten post https://bugzilla.mozilla.org/show_bug.cgi?id=607863 szczegółowo omawia bieżące implementacje
__proto__
i różnice między nimi. Każda implementacja robi to inaczej, ponieważ jest to trudny i nierozwiązany problem. Wszystko w Javascript jest zmienne, z wyjątkiem a.) Składni b.) Obiektów hosta (technicznie rzecz biorąc, DOM istnieje poza JavaScriptem) ic.)__proto__
. Reszta jest całkowicie w rękach Ciebie i każdego innego programisty, więc możesz zobaczyć, dlaczego__proto__
wystaje jak bolący kciuk.Jest jedna rzecz, która
__proto__
pozwala na to, co w innym przypadku jest niemożliwe: wyznaczenie prototypu obiektów w czasie wykonywania, niezależnie od jego konstruktora. Jest to ważny przypadek użycia i jeden z głównych powodów, dla których__proto__
nie jest martwy. Na tyle ważne, że był to poważny punkt dyskusji przy formułowaniu Harmony lub wkrótce będzie znany jako ECMAScript 6. Możliwość określenia prototypu obiektu podczas tworzenia będzie częścią kolejnej wersji Javascript i będzie to dzwon wskazujący__proto__
dni są formalnie numerowane.Na krótką metę możesz użyć,
__proto__
jeśli kierujesz reklamy na przeglądarki, które go obsługują (nie IE i żaden IE nigdy tego nie zrobi). Prawdopodobnie będzie działać w webkit i moz przez następne 10 lat, ponieważ ES6 nie zostanie sfinalizowany do 2013 roku.Brendan Eich - re: Approach of new Object methods in ES5 :
źródło
non-writable, configurable
rozwiązałoby te problemy, zmuszając użytkownika do jawnej rekonfiguracji właściwości. Ostatecznie złe praktyki wiążą się z nadużywaniem możliwości języka, a nie samych możliwości. Zapisywalny __proto__ nie jest czymś niezwykłym. Istnieje wiele innych niewliczalnych właściwości zapisywalnych i chociaż istnieją zagrożenia, istnieją również najlepsze praktyki. Nie należy zdejmować łba młotka tylko dlatego, że może być niewłaściwie użyta do zranienia kogoś.__proto__
jest potrzebna, ponieważ Object.create będzie wytwarzać tylko obiekty, a nie funkcje, na przykład symbole, reguły, elementy DOM lub inne obiekty hosta. Jeśli chcesz, aby twoje obiekty były wywoływalne lub wyjątkowe w inny sposób, ale nadal zmieniasz ich łańcuch prototypów, utkniesz bez ustawiania__proto__
lub proxy__proto__
problem „ w JSON”. Inne obawy Brendana Eicha są śmieszne. Rekurencyjne łańcuchy prototypów lub użycie prototypu z nieadekwatnymi metodami do danego obiektu są błędami programisty, a nie błędami językowymi i nie powinny być czynnikiem, a poza tym mogą się zdarzyć z Object.create równie dobrze, jak z dowolnie ustawianymi__proto__
.__proto__
.ES6 ostatecznie określa Object.setPrototypeOf (obiekt, prototyp), który jest już zaimplementowany w Chrome i Firefox.
źródło
Możesz użyć
constructor
na instancji obiektu, aby zmienić prototyp obiektu w miejscu. Myślę, że o to właśnie prosisz.Oznacza to, że jeśli masz,
foo
który jest wystąpieniemFoo
:Możesz dodać właściwość
bar
do wszystkich instancji programuFoo
, wykonując następujące czynności:Oto skrzypce pokazujące dowód słuszności koncepcji: http://jsfiddle.net/C2cpw/ . Nie jestem do końca pewien, jak starsze przeglądarki poradzą sobie z tym podejściem, ale jestem prawie pewien, że powinno to całkiem dobrze wykonać zadanie.
Jeśli Twoim zamiarem jest dodanie funkcji do obiektów, ten fragment powinien wystarczyć:
źródło
foo.__proto__.bar = 'bar';
Możesz to zrobić
foo.__proto__ = FooClass.prototype
, AFAIK obsługiwany przez przeglądarki Firefox, Chrome i Safari. Należy pamiętać, że__proto__
nieruchomość jest niestandardowa i może w pewnym momencie zniknąć.Dokumentacja: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto . Zobacz też http://www.mail-archive.com/[email protected]/msg00392.html, aby uzyskać wyjaśnienie, dlaczego nie istnieje
Object.setPrototypeOf()
i dlaczego__proto__
jest przestarzałe.źródło
Można zdefiniować funkcję konstruktora proxy, a następnie utworzyć nową instancję i skopiować do niej wszystkie właściwości z oryginalnego obiektu.
Demo na żywo: http://jsfiddle.net/6Xq3P/
Custom
Konstruktor reprezentuje nowy prototyp, ergo, jegoCustom.prototype
przedmiot zawiera wszystkie nowe właściwości których chcesz korzystać z oryginalnego obiektu.Wewnątrz
Custom
konstruktora wystarczy skopiować wszystkie właściwości z oryginalnego obiektu do nowego obiektu instancji.Ten nowy obiekt instancji zawiera wszystkie właściwości z oryginalnego obiektu (zostały do niego skopiowane wewnątrz konstruktora), a także wszystkie nowe właściwości zdefiniowane wewnątrz
Custom.prototype
(ponieważ nowy obiekt jestCustom
instancją).źródło
Nie możesz zmienić prototypu obiektu JavaScript, którego instancja została już utworzona za pomocą różnych przeglądarek. Jak wspominali inni, dostępne opcje obejmują:
__proto__
nieruchomośćŻadne z nich nie są szczególnie świetne, zwłaszcza jeśli musisz rekurencyjnie przechodzić przez obiekt do obiektów wewnętrznych, aby skutecznie zmienić cały prototyp elementów.
Alternatywne rozwiązanie pytania
Przyjrzę się bardziej abstrakcyjnie na funkcjonalność, której pragniesz.
Zasadniczo prototyp / metody po prostu pozwalają na grupowanie funkcji na podstawie obiektu.
Zamiast pisać
ty piszesz
Powyższa składnia została stworzona jako OOP ze względu na składnię object.method (). Niektóre z głównych zalet OOP w porównaniu z tradycyjnym programowaniem funkcjonalnym obejmują:
obj.replace('needle','replaced')
a konieczność zapamiętywania nazw takich jakstr_replace ( 'foo' , 'bar' , 'subject')
i lokalizacji różnych zmiennychstring.trim().split().join()
) jest potencjalnie łatwiejszą do zmodyfikowania i zapisania niż funkcje zagnieżdżonejoin(split(trim(string))
Niestety w JavaScript (jak pokazano powyżej) nie można modyfikować już istniejącego prototypu. Najlepiej byłoby, gdyby powyżej można było modyfikować
Object.prototype
tylko powyższe obiekty, ale niestety modyfikowanieObject.prototype
mogłoby potencjalnie zepsuć skrypty (powodując kolizję właściwości i nadpisywanie).Nie ma powszechnie używanego środka pośredniego między tymi dwoma stylami programowania i nie ma sposobu OOP na organizację funkcji niestandardowych.
UnlimitJS zapewnia kompromis umożliwiający definiowanie niestandardowych metod. Unika:
Używając powyższego kodu, po prostu utworzyłbym przestrzeń nazw funkcji, które zamierzasz wywołać względem obiektu.
Oto przykład:
Więcej przykładów możesz przeczytać tutaj UnlimitJS . Zasadniczo, gdy wywołujesz
[Unlimit]()
funkcję, umożliwia wywołanie funkcji jako metody w obiekcie. To jak środek między OOP a funkcjonalnymi drogami.źródło
[[prototype]]
O ile wiem, nie można zmienić odniesienia do już zbudowanych obiektów. Mógłbyś zmienić właściwość prototypu oryginalnej funkcji konstruktora, ale, jak już zauważyłeś, tym konstruktorem jestObject
, a modyfikowanie podstawowych konstrukcji JS jest złą rzeczą.Możesz jednak utworzyć obiekt proxy skonstruowanego obiektu, który implementuje dodatkową funkcjonalność, której potrzebujesz. Możesz także zastosować monkeypatch dodatkowe metody i zachowania, przypisując je bezpośrednio do danego obiektu.
Być może możesz dostać to, czego chcesz, w inny sposób, jeśli chcesz podejść z innego punktu widzenia: Co musisz zrobić, aby bawić się prototypem?
źródło
__proto__
właściwości. Będzie to działać tylko w przeglądarkach obsługujących__proto__
notację, czyli Chrome i Firefox, i zostało wycofane . Krótko mówiąc, możesz zmienić[[prototype]]
obiekt, ale prawdopodobnie nie powinieneś.Jeśli znasz prototyp, dlaczego nie wstrzyknąć go w kodzie?
Tak więc, gdy dane zostaną zserializowane, otrzymasz
teraz potrzebujesz tylko konstruktora, który przyjmuje tablicę jako argument.
źródło
Nie ma sposobu, aby tak naprawdę odziedziczyć
Array
lub „podklasować” to.Możesz to zrobić ( OSTRZEŻENIE: FESTERING CODE AHEAD ):
To działa, ale spowoduje pewne problemy dla każdego, kto przecina jego ścieżkę (wygląda jak tablica, ale jeśli spróbujesz nią manipulować, wszystko pójdzie nie tak).
Dlaczego nie pójdziesz rozsądną drogą i nie dodasz metod / właściwości bezpośrednio do
foo
lub nie użyjesz konstruktora i nie zapiszesz swojej tablicy jako właściwości?źródło
jeśli chcesz stworzyć prototyp w locie, to jest jedna z możliwości
źródło
źródło
prototype
jest statyczny? Nie jestem pewny co masz na myśli.prototype
jest właściwością funkcji, a nie obiektem.Object.prototype
istnieje;{}.prototype
nie.Object.getPrototypeOf(foo)
do pobrania prototypowego obiektu konstruktora. Możesz modyfikować właściwości, aby zmodyfikować prototyp obiektu. Działa tylko w najnowszych przeglądarkach, ale nie można powiedzieć, które z nich.Object.prototype
bezpośrednio, ponieważ najprawdopodobniej toObject.getPrototypeOf(foo)
powróci. Niezbyt przydatne.