JavaScript не имеет точного аналога для методов расширения C #. JavaScript и C # - совершенно разные языки.
Ближайшая подобная вещь, чтобы изменить объект - прототип всех строковых объектов: String.prototype
. В общем, лучшая практика - не изменять прототипы встроенных объектов в коде библиотеки, предназначенные для объединения с другим кодом, который вы не контролируете. (Сделать это в приложении, где вы контролируете, какой еще код включается в приложение, нормально.)
Если вы делаете изменения прототипа встроенной в, это лучше всего (на сегодняшний день) , чтобы сделать это в несчетное свойство, используя Object.defineProperty
(ES5 +, поэтому в основном любую современную среду JavaScript, а не IE8¹ или ранее). Чтобы соответствовать перечисляемости, возможности записи и настройки других строковых методов, это будет выглядеть так:
Object.defineProperty(String.prototype, "SayHi", {
value: function SayHi() {
return "Hi " + this + "!";
},
writable: true,
configurable: true
});
(По умолчанию enumerable
это false
.)
Если вам нужно поддерживать устаревшие среды, то String.prototype
, в частности, вам, вероятно, удастся создать перечислимое свойство:
String.prototype.SayHi = function SayHi() {
return "Hi " + this + "!";
};
Это плохая идея, но вам может сойти с рук. Никогда не делайте этого с помощью Array.prototype
или Object.prototype
; создание на них перечислимых свойств - Плохая вещь.
Детали:
JavaScript - это прототип языка. Это означает, что каждый объект поддерживается объектом-прототипом . В JavaScript этот прототип назначается одним из четырех способов:
- С помощью функции-конструктора для объекта (например,
new Foo
создает объект в Foo.prototype
качестве прототипа)
- С помощью
Object.create
функции, добавленной в ES5 (2009 г.)
- По
__proto__
свойству аксессуара (ES2015 +, только в веб-браузерах, существовало в некоторых средах до стандартизации) или Object.setPrototypeOf
(ES2015 +)
- Механизмом JavaScript при создании объекта для примитива, потому что вы вызываете для него метод (это иногда называется «продвижение»).
Итак, в вашем примере, поскольку firstName
это строковый примитив, он становится String
экземпляром всякий раз, когда вы вызываете для него метод, а String
прототип этого экземпляра String.prototype
. Итак, добавив свойство вString.prototype
ссылкам на вашу SayHi
функцию делает эту функцию доступной для всех String
экземпляров (и, по сути, для строковых примитивов, потому что они продвигаются).
Пример:
Object.defineProperty(String.prototype, "SayHi", {
value: function SayHi() {
return "Hi " + this + "!";
},
writable: true,
configurable: true
});
console.log("Charlie".SayHi());
Между этим и C # методами расширения есть несколько ключевых отличий:
(Как указал DougR в комментарии) Методы расширения C # можно вызывать по null
ссылкам . Если у вас есть string
метод расширения, этот код:
string s = null;
s.YourExtensionMethod();
работает. Это не так с JavaScript; null
является его собственным типом, и любая ссылка на свойство null
вызывает ошибку. (И даже если бы этого не произошло, прототипа для расширения типа Null не существует.)
(Как отметил ChrisW в комментарии) Методы расширения C # не являются глобальными. Они доступны, только если пространство имен, в котором они определены, используется кодом с помощью метода расширения. (Они на самом деле являются синтаксическим сахаром для статических вызовов, поэтому они работают null
.) Это неверно в JavaScript: если вы измените прототип встроенного модуля, это изменение будет замечено всем кодом во всей области, в которой вы находитесь. сделайте это в (область - это глобальная среда и связанные с ней внутренние объекты и т. д.). Поэтому, если вы сделаете это на веб-странице, весь код, который вы загружаете на этой странице, увидит изменение. Если вы сделаете это в модуле Node.js, всекод, загруженный в той же области, что и этот модуль, увидит изменение. В обоих случаях, поэтому вы не делаете этого в коде библиотеки. (Веб-воркеры и рабочие потоки Node.js загружаются в свою собственную область, поэтому у них другая глобальная среда и другие встроенные функции, чем у основного потока. Но эта область по-прежнему используется всеми модулями, которые они загружают.)
¹ IE8 есть Object.defineProperty
, но он работает только с объектами DOM, а не с объектами JavaScript. String.prototype
это объект JavaScript.
String s = null;
части был в использованииs.YourExtensionMethod()
! Оцените улов. :-)class MyArray extends Array { singleOrDefault() { ... }}
), но это означает, что вы должны сделать этоMyArray.from(originalArray)
перед его использованием, еслиoriginalArray
это утомительный старый массив. Существуют также LINQ-подобные библиотеки для JavaScript ( вот обзор ), которые аналогичным образом включают создание объекта Linq из массива перед выполнением каких-либо действий (ну, LINQ to JavaScript сделал, я не использовал другие [и не использовал тот годами]). (Кстати, обновил ответ, спасибо.)Использование глобального увеличения
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 };
источник