我需要在JS中编写一些扩展方法。我知道如何在C#中做到这一点。例如:
public static string SayHi(this Object name)
{
return "Hi " + name + "!";
}
然后通过以下方式调用:
string firstName = "Bob";
string hi = firstName.SayHi();
我如何在JavaScript中执行此类操作?
答案 0 :(得分:121)
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
,具体而言,您可能会创建一个可枚举的属性:
// Don't do this if you can use `Object.defineProperty`
String.prototype.SayHi = function SayHi() {
return "Hi " + this + "!";
};
这不是一个好主意,但你可能会侥幸逃脱。 从不使用Array.prototype
或Object.prototype
执行此操作;在那些上创建可枚举的属性是Bad Thing™。
详细说明:
JavaScript是一种原型语言。这意味着每个对象都由原型对象支持。在JavaScript中,该原型以四种方式之一分配:
new Foo
创建一个以Foo.prototype
为原型的对象)Object.create
功能
__proto__
访问者属性(ES2015 +,仅限于网络浏览器,在标准化之前存在于某些环境中)或Object.setPrototypeOf
(ES2015 +)因此,在您的示例中,由于firstName
是一个字符串原语,只要您在其上调用方法,它就会被提升为String
实例,并且String
实例的原型是{{ 1}}。因此,向String.prototype
添加一个引用您的String.prototype
函数的属性会使该函数在所有SayHi
实例上可用(并且对字符串原语有效,因为它们会被提升)。
示例:
String
这个和C#扩展方法之间存在一些关键差异:
(在评论中指出DougR) C#的扩展方法can be called on null
references。如果您使用Object.defineProperty(String.prototype, "SayHi", {
value: function SayHi() {
return "Hi " + this + "!";
},
writable: true,
configurable: true
});
console.log("Charlie".SayHi());
扩展方法,则此代码为:
string
的工作原理。 JavaScript的情况并非如此; string s = null;
s.YourExtensionMethod();
是它自己的类型,null
上的任何属性引用都会引发错误。 (即使它没有,也没有为Null类型扩展的原型。)
(在评论中指出为ChrisW) C#的扩展方法不是全局的。只有在使用扩展方法的代码使用它们所定义的命名空间时,它们才可访问。 (它们实际上是静态调用的语法糖,这就是它们在null
上工作的原因。)在JavaScript中并非如此:如果更改内置的原型,那么你所做的整个领域中的所有代码(领域是全局环境及其相关的内部对象等)。因此,如果您在网页中执行此操作,则您在该网页上加载的所有代码会看到更改。如果您在Node.js模块中执行此操作,则在与该模块相同的域中加载的所有代码将看到更改。在这两种情况下,这就是你不在库代码中执行此操作的原因。 (Web worker和Node.js工作线程在它们自己的领域中加载,因此它们具有与主线程不同的全局环境和不同的内在函数。但是该领域仍然与它们加载的任何模块共享。 )
¹IE8确实有null
,但它只适用于DOM对象,而不适用于JavaScript对象。 Object.defineProperty
是一个JavaScript对象。
答案 1 :(得分:0)
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
};