繁体   English   中英

如何在 JavaScript 中编写扩展方法?

[英]How do I write an extension method in 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 通常,最佳实践是不要修改库代码中旨在与您无法控制的其他代码组合的内置对象的原型。 (在您控制应用程序中包含哪些其他代码的应用程序中执行此操作是可以的。)

如果您确实修改了内置的原型,最好(到目前为止)通过使用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.prototypeObject.prototype这样做; 在这些上创建可枚举的属性是一件坏事™。

细节:

JavaScript 是一种原型语言。 这意味着每个对象都由原型对象支持。 在 JavaScript 中,该原型以四种方式之一分配:

  • 通过对象的构造函数(例如, new Foo创建了一个以Foo.prototype为原型的对象)
  • 通过 ES5 (2009) 中添加的Object.create函数
  • 通过__proto__访问器属性(ES2015+,仅在 Web 浏览器上,在标准化之前存在于某些环境中)或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 模块中执行此操作,则在与该模块相同的领域中加载的所有代码都将看到更改。 在这两种情况下,这就是您不在库代码中执行此操作的原因。 (Web 工作线程和 Node.js 工作线程加载在它们自己的领域中,因此它们具有与主线程不同的全局环境和不同的内在函数。但该领域仍然与它们加载的任何模块共享。)


¹ 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
};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM