[英]How do I write an extension method in JavaScript?
I need to write a few extension methods in JS.我需要在JS中编写一些扩展方法。 I know just how to do this in C#.我知道如何在 C# 中做到这一点。 Example:例子:
public static string SayHi(this Object name)
{
return "Hi " + name + "!";
}
and then called by:然后通过以下方式调用:
string firstName = "Bob";
string hi = firstName.SayHi();
How would I do something like this in JavaScript?我将如何在 JavaScript 中做这样的事情?
JavaScript doesn't have an exact analogue for C#'s extension methods. JavaScript 没有与 C# 的扩展方法完全类似的东西。 JavaScript and C# are quite different languages. JavaScript 和 C# 是完全不同的语言。
The nearest similar thing is to modify the prototype object of all string objects: String.prototype
.最接近类似的就是修改所有字符串对象的原型对象: String.prototype
。 In general, best practice is not to modify the prototypes of built-in objects in library code meant to be combined with other code you don't control.通常,最佳实践是不要修改库代码中旨在与您无法控制的其他代码组合的内置对象的原型。 (Doing it in an application where you control what other code is included in the application is okay.) (在您控制应用程序中包含哪些其他代码的应用程序中执行此操作是可以的。)
If you do modify the prototype of a built-in, it's best (by far) to make that a non-enumerable property by using Object.defineProperty
(ES5+, so basically any modern JavaScript environment, and not IE8¹ or earlier).如果您确实修改了内置的原型,最好(到目前为止)通过使用Object.defineProperty
(ES5+,所以基本上是任何现代 JavaScript 环境,而不是 IE8¹ 或更早版本)使其成为不可枚举的属性。 To match the enumerability, writability, and configurability of other string methods, it would look like this:为了匹配其他字符串方法的可枚举性、可写性和可配置性,它看起来像这样:
Object.defineProperty(String.prototype, "SayHi", {
value: function SayHi() {
return "Hi " + this + "!";
},
writable: true,
configurable: true
});
(The default for enumerable
is false
.) ( enumerable
的默认值为false
。)
If you needed to support obsolete environments, then for String.prototype
, specifically, you could probably get away with creating an enumerable property:如果您需要支持过时的环境,那么对于String.prototype
,特别是,您可能会创建一个可枚举的属性:
// Don't do this if you can use `Object.defineProperty`
String.prototype.SayHi = function SayHi() {
return "Hi " + this + "!";
};
That's not a good idea, but you might get away with it.这不是一个好主意,但你可能会侥幸逃脱。 Never do that with Array.prototype
or Object.prototype
;永远不要用Array.prototype
或Object.prototype
这样做; creating enumerable properties on those is a Bad Thing™.在这些上创建可枚举的属性是一件坏事™。
Details:细节:
JavaScript is a prototypical language. JavaScript 是一种原型语言。 That means that every object is backed by a prototype object .这意味着每个对象都由原型对象支持。 In JavaScript, that prototype is assigned in one of four ways:在 JavaScript 中,该原型以四种方式之一分配:
new Foo
creates an object with Foo.prototype
as its prototype)通过对象的构造函数(例如, new Foo
创建了一个以Foo.prototype
为原型的对象)Object.create
function added in ES5 (2009)通过 ES5 (2009) 中添加的Object.create
函数__proto__
accessor property (ES2015+, only on web browsers, existed in some environments before it was standardized) or Object.setPrototypeOf
(ES2015+)通过__proto__
访问器属性(ES2015+,仅在 Web 浏览器上,在标准化之前存在于某些环境中)或Object.setPrototypeOf
(ES2015+) So in your example, since firstName
is a string primitive, it gets promoted to a String
instance whenever you call a method on it, and that String
instance's prototype is String.prototype
.因此,在您的示例中,由于firstName
是字符串原语,因此每当您对其调用方法时,它都会被提升为String
实例,并且该String
实例的原型是String.prototype
。 So adding a property to String.prototype
that references your SayHi
function makes that function available on all String
instances (and effectively on string primitives, because they get promoted).因此,向String.prototype
添加一个引用您的SayHi
函数的属性,可以使该函数在所有String
实例上可用(并且在字符串原语上有效,因为它们得到提升)。
Example:例子:
Object.defineProperty(String.prototype, "SayHi", { value: function SayHi() { return "Hi " + this + "!"; }, writable: true, configurable: true }); console.log("Charlie".SayHi());
There are some key differences between this and C# extension methods:此方法与 C# 扩展方法之间存在一些主要区别:
(As DougR pointed out in a comment) C#'s extension methods can be called on null
references . (正如DougR在评论中指出的那样) C# 的扩展方法可以在null
引用上调用。 If you have a string
extension method, this code:如果您有string
扩展方法,则此代码:
string s = null; s.YourExtensionMethod();
works.作品。 That isn't true with JavaScript; JavaScript 不是这样。 null
is its own type, and any property reference on null
throws an error. null
是它自己的类型,任何对null
属性引用都会引发错误。 (And even if it didn't, there's no prototype to extend for the Null type.) (即使没有,也没有原型可以扩展为 Null 类型。)
(As ChrisW pointed out in a comment) C#'s extension methods aren't global. (正如ChrisW在评论中指出的那样) C# 的扩展方法不是全局的。 They're only accessible if the namespace they're defined in is used by the code using the extension method.只有当代码使用扩展方法使用它们定义的命名空间时,它们才可访问。 (They're really syntactic sugar for static calls, which is why they work on null
.) That isn't true in JavaScript: If you change the prototype of a built-in, that change is seen by all code in the entire realm you do that in (a realm is the global environment and its associated intrinsic objects, etc.). (它们确实是静态调用的语法糖,这就是它们在null
工作的原因。)在 JavaScript 中并非如此:如果您更改内置函数的原型,则整个领域中的所有代码都会看到该更改你这样做(领域是全球环境及其相关的内在对象等)。 So if you do this in a web page, all code you load on that page sees the change.因此,如果您在网页中执行此操作,则您在该页面上加载的所有代码都会看到更改。 If you do this in a Node.js module, all code loaded in the same realm as that module will see the change.如果您在 Node.js 模块中执行此操作,则在与该模块相同的领域中加载的所有代码都将看到更改。 In both cases, that's why you don't do this in library code.在这两种情况下,这就是您不在库代码中执行此操作的原因。 (Web workers and Node.js worker threads are loaded in their own realm, so they have a different global environment and different intrinsics than the main thread. But that realm is still shared with any modules they load.) (Web 工作线程和 Node.js 工作线程加载在它们自己的领域中,因此它们具有与主线程不同的全局环境和不同的内在函数。但该领域仍然与它们加载的任何模块共享。)
¹ IE8 does have Object.defineProperty
, but it only works on DOM objects, not JavaScript objects. ¹ IE8 确实有Object.defineProperty
,但它只适用于 DOM 对象,而不适用于 JavaScript 对象。 String.prototype
is a JavaScript object. String.prototype
是一个 JavaScript 对象。
Using Global augmentation使用全局增强
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
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.