JavaScript nie ma dokładnego odpowiednika metod rozszerzających języka C #. JavaScript i C # to całkiem różne języki.
Najbliższa podobna sprawa jest zmodyfikowanie obiektu prototypu wszystkich obiektów String: String.prototype
. Ogólnie rzecz biorąc, najlepszą praktyką jest nie modyfikowanie prototypów obiektów wbudowanych w kodzie biblioteki, które mają być łączone z innym kodem, nad którym nie masz kontroli. (Zrobienie tego w aplikacji, w której kontrolujesz, jaki inny kod jest zawarty w aplikacji, jest w porządku).
Jeśli zrobić zmodyfikować prototyp wbudowany, najlepiej (jak dotąd), aby to się nie enumerable własność za pomocą Object.defineProperty
(ES5 +, więc w zasadzie każdego nowoczesnego środowiska JavaScript, a nie IE8¹ lub wcześniej). Aby dopasować wyliczalność, zapisywalność i konfigurowalność innych metod łańcuchowych, wyglądałoby to następująco:
Object.defineProperty(String.prototype, "SayHi", {
value: function SayHi() {
return "Hi " + this + "!";
},
writable: true,
configurable: true
});
(Wartość domyślna enumerable
to false
.)
Jeśli potrzebujesz obsługiwać przestarzałe środowiska, to w String.prototype
szczególności prawdopodobnie możesz uciec od utworzenia wyliczalnej właściwości:
String.prototype.SayHi = function SayHi() {
return "Hi " + this + "!";
};
To nie jest dobry pomysł, ale może ci się to udać. Nigdy nie rób tego z Array.prototype
lub Object.prototype
; tworzenie na nich niezliczonych właściwości jest złą rzeczą ™.
Detale:
JavaScript to język prototypowy. Oznacza to, że każdy obiekt jest obsługiwany przez obiekt prototypowy . W JavaScript ten prototyp jest przypisywany na jeden z czterech sposobów:
- Przez funkcję konstruktora dla obiektu (np.
new Foo
Tworzy obiekt z Foo.prototype
prototypem)
- Według
Object.create
funkcji dodanej w ES5 (2009)
- Według
__proto__
właściwości accessor (ES2015 +, tylko w przeglądarkach internetowych, istniała w niektórych środowiskach, zanim została ujednolicona) lub Object.setPrototypeOf
(ES2015 +)
- Przez silnik JavaScript podczas tworzenia obiektu dla prymitywu, ponieważ wywołujesz na nim metodę (czasami nazywa się to „promocją”)
Więc w twoim przykładzie, ponieważ firstName
jest to ciąg znaków, jest promowany do String
instancji za każdym razem, gdy wywołujesz na niej metodę, a String
jej prototypem jest String.prototype
. Zatem dodanie właściwości do String.prototype
tej SayHi
funkcji, która odwołuje się do funkcji, sprawia, że ta funkcja jest dostępna we wszystkich String
instancjach (i efektywnie na elementach łańcuchowych, ponieważ są promowane).
Przykład:
Object.defineProperty(String.prototype, "SayHi", {
value: function SayHi() {
return "Hi " + this + "!";
},
writable: true,
configurable: true
});
console.log("Charlie".SayHi());
Istnieją pewne kluczowe różnice między this i metodami rozszerzenia języka C #:
(Jak zauważył DougR w komentarzu) Metody rozszerzające języka C # można wywoływać w null
odwołaniach . Jeśli masz string
metodę rozszerzenia, ten kod:
string s = null;
s.YourExtensionMethod();
Pracuje. To nie jest prawdą w przypadku JavaScript; null
jest własnym typem, a każde odwołanie do właściwości w przypadku null
zgłasza błąd. (A nawet gdyby tak się nie stało, nie ma prototypu do rozszerzenia dla typu Null).
(Jak wskazał ChrisW w komentarzu) Metody rozszerzające języka C # nie są globalne. Są dostępne tylko wtedy, gdy przestrzeń nazw, w której są zdefiniowane, jest używana przez kod przy użyciu metody rozszerzenia. (Są tak naprawdę cukrem syntaktycznym dla wywołań statycznych, dlatego też nad nimi pracują null
.) To nieprawda w JavaScript: jeśli zmienisz prototyp elementu wbudowanego, ta zmiana będzie widoczna dla całego kodu w całej dziedzinie, zrób to w (sfera to globalne środowisko i związane z nim wewnętrzne obiekty itp.). Więc jeśli zrobisz to na stronie internetowej, cały kod, który załadujesz na tej stronie, zobaczy zmianę. Jeśli zrobisz to w module Node.js, wszystkie plikikod załadowany w tej samej dziedzinie, co ten moduł, zobaczy zmianę. W obu przypadkach dlatego nie robisz tego w kodzie biblioteki. (Pracownicy sieci i node.js wątków roboczych są ładowane we własnym królestwie, więc mają różne środowiska i różne intrinsics globalny niż głównego wątku. Ale królestwo jest nadal udostępniane żadnym modułów one obciążeniu.)
¹ IE8 tak jest Object.defineProperty
, ale działa tylko na obiektach DOM, a nie na obiektach JavaScript. String.prototype
jest obiektem JavaScript.
String s = null;
części było użycies.YourExtensionMethod()
! Doceń haczyk. :-)class MyArray extends Array { singleOrDefault() { ... }}
), ale oznacza to, że musisz to zrobićMyArray.from(originalArray)
przed jej użyciem, jeślioriginalArray
jest to nudna stara tablica. Istnieją również biblioteki podobne do LINQ dla JavaScript ( tutaj jest podsumowanie ), które podobnie obejmują utworzenie obiektu Linq z tablicy przed wykonaniem czynności (cóż, LINQ to JavaScript zrobił, nie używałem innych [i nie użyłem tego w latach]). (BTW, zaktualizowałem odpowiedź, dzięki.)Korzystanie z globalnego wzmocnienia
declare global { interface DOMRect { contains(x:number,y:number): boolean; } } /* Adds contain method to class DOMRect */ DOMRect.prototype.contains = function (x:number, y:number) { if (x >= this.left && x <= this.right && y >= this.top && y <= this.bottom) { return true } return false };
źródło