我需要在JS中编写一些扩展方法。我知道如何在 C# 中做到这一点。例子:
public static string SayHi(this Object name)
{
return "Hi " + name + "!";
}
然后通过以下方式调用:
string firstName = "Bob";
string hi = firstName.SayHi();
我将如何在 JavaScript 中做这样的事情?
我需要在JS中编写一些扩展方法。我知道如何在 C# 中做到这一点。例子:
public static string SayHi(this Object name)
{
return "Hi " + name + "!";
}
然后通过以下方式调用:
string firstName = "Bob";
string hi = firstName.SayHi();
我将如何在 JavaScript 中做这样的事情?
JavaScript 没有与 C# 的扩展方法完全类似的东西。JavaScript 和 C# 是完全不同的语言。
最接近类似的就是修改所有字符串对象的原型对象:String.prototype
. 通常,最佳实践是不要修改库代码中旨在与您无法控制的其他代码组合的内置对象的原型。(在您控制应用程序中包含哪些其他代码的应用程序中执行此操作是可以的。)
如果您确实修改了内置的原型,最好(到目前为止)通过使用(ES5+,所以基本上是任何现代 JavaScript 环境,而不是 IE8¹ 或更早版本)使其成为不可枚举的属性Object.defineProperty
。为了匹配其他字符串方法的可枚举性、可写性和可配置性,它看起来像这样:
Object.defineProperty(String.prototype, "SayHi", {
value: function SayHi() {
return "Hi " + this + "!";
},
writable: true,
configurable: true
});
(默认为enumerable
IS false
)。
如果您需要支持过时的环境,那么 for String.prototype
,特别是,您可能可以通过创建可枚举属性而逃脱:
// Don't do this if you can use `Object.defineProperty`
String.prototype.SayHi = function SayHi() {
return "Hi " + this + "!";
};
这不是一个好主意,但你可能会侥幸逃脱。永远不要用Array.prototype
或那样做Object.prototype
;在这些上创建可枚举的属性是一件坏事™。
细节:
JavaScript 是一种原型语言。这意味着每个对象都由原型对象支持。在 JavaScript 中,该原型以四种方式之一分配:
new Foo
创建一个对象Foo.prototype
作为其原型)Object.create
ES5 (2009) 中添加的功能__proto__
访问器属性(ES2015+,仅在 Web 浏览器上,在标准化之前存在于某些环境中)或Object.setPrototypeOf
(ES2015+)因此,在您的示例中,由于firstName
是字符串原语,因此String
每当您对其调用方法时,它都会被提升为实例,并且该String
实例的原型是String.prototype
. 因此,向String.prototype
引用您的SayHi
函数的属性添加一个属性可以使该函数在所有String
实例上可用(并且在字符串基元上有效,因为它们得到了提升)。
例子:
此方法与 C# 扩展方法之间存在一些主要区别:
(正如DougR在评论中指出的那样) C# 的扩展方法可以在null
引用上调用。如果您有string
扩展方法,则此代码:
string s = null;
s.YourExtensionMethod();
工作(除非YourExtensionMethod
它null
作为实例参数接收时抛出)。JavaScript 不是这样。null
是它自己的类型,任何属性引用都会null
引发错误。(即使没有,也没有原型可以扩展为 Null 类型。)
null
)那不是真的在JavaScript:如果您更改的原型内置,该变化是由看到所有在整个领域的代码,你这样做(领域是全球环境及其相关的内在对象等)。因此,如果您在网页中执行此操作,您在该页面上加载的所有代码都会看到更改。如果您在 Node.js module中执行此操作,则所有在与该module相同的领域中加载的代码将看到更改。在这两种情况下,这就是您不在库代码中执行此操作的原因。(Web 工作线程和 Node.js 工作线程加载在它们自己的领域中,因此它们具有与主线程不同的全局环境和不同的内在函数。但该领域仍然与它们加载的任何module共享。)¹ IE8 确实有Object.defineProperty
,但它只适用于 DOM 对象,而不适用于 JavaScript 对象。String.prototype
是一个 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
};