【问题标题】:Adding a method to a class method in typescript (javascript)在打字稿(javascript)中向类方法添加方法
【发布时间】:2019-03-11 05:51:18
【问题描述】:

我想在方法中添加一个方法:

class MyClass {
    public foo(text: string): string {
        return text + ' FOO!'
    }

    // Some magical code which adds the method `bar` to `foo`.
}

const obj = new MyClass();
console.log(obj.foo('thing'));
console.log(obj.foo.bar('other thing'));

这可能吗?我为函数找到了similar case

function Sum(x: number) { /*...*/ }
namespace Sum {
    export function empty() { /*...*/ }
}

有没有办法对类的方法做同样的事情?

我希望在类中包含代码,而不是在创建对象后进行猴子补丁。

【问题讨论】:

    标签: javascript typescript methods


    【解决方案1】:

    您可以访问类原型上的函数并使用它做任何您喜欢的事情。我不认为它特别漂亮,但你可以这样做:

    class MyClass {
      constructor(myName) {
        this.myName = myName
        this.constructor.prototype.foo.bar = (function(a) {
          console.log(this.myName, "calling foo.bar with:", a)
        }).bind(this)
      }
      foo(text) {
        return text + ' FOO!'
      }
    
    }
    
    const obj = new MyClass("Mark");
    obj.foo.bar('thing')
    console.log(obj.foo('test'))

    【讨论】:

      【解决方案2】:

      不完全符合您的要求,但最终结果有效:

      class MyClass {
        public foo: MyChildClass = new MyChildClass();
      }
      
      class MyChildClass {
        public bar(text: string): string{
          return text;
        }
      }
      
      const obj = new MyClass();
      console.log(obj.foo.bar('thing'));
      

      编辑我阅读了您对我的评论的回答。我认为实现目标的更简单方法是使用如下默认参数:

      function foo(text: string, snapshot = false): string{
        if(snapshot){
          return 'snapshot';
        }
        return text;
      }
      
      console.debug(foo('test'));
      console.debug(foo('test', true));
      

      现在,您的解决方案的优势在于,您可以在调用站点清楚地看到您正在请求绕过或快照,因为附加了函数名称。您可以通过将 foo 的参数替换为具有可选属性的接口来在 typescript 中实现类似的结果。在其他语言中,我们将这种技术称为命名参数:

      interface iFooArgs{
        text: string;
        bypass?: boolean;
        snapshot?: boolean;
      }
      
      function foo(args: iFooArgs): string {
        if(args.bypass){
          return 'bypass';
        }
        if(args.snapshot){
          return 'snapshot';
        }
        return args.text;
      }
      
      console.debug(foo({text: 'test'}));
      console.debug(foo({text: 'bypass?', bypass: true}));
      console.debug(foo({text: 'snapshot?', snapshot: true}));
      

      【讨论】:

      • foo 作为一种方法是我想要实现的核心。我希望 foo() 方法成为方法的默认用法。它可能是一种通过内置缓存访问昂贵资源的方法。您可能喜欢使用该方法的另一种方式,例如绕过或使缓存无效。可能您通常使用obj.fetch('42'),但有时您可能希望obj.fetch.bypass('42') 强制加载昂贵的资源,或者obj.fetch.snapshot('42') 强制使用缓存,即使缓存已超时。在 python 中,我只是将您的示例与__call__(self) 一起使用。
      • @AndréC.Andersen 这很有趣。我认为您在 js 中执行此操作的方式通常只是让 foo 包含一个绕过缓存的参数。不提供“绕过缓存”参数的 foo 消费者将接收缓存。 foo() 本身会验证是否提供了参数,或者默认为“未定义”用于您的“默认用法”。
      • @AndréC.Andersen 在我的回答中添加了我认为更简单的问题解决方案。
      【解决方案3】:

      更新:这可以使用交叉点类型彻底解决:

      class MyClass {
      
          constructor() {
              this.foo = function () {
                  return "this is a"
              };
      
              this.foo.bar = function() {
                  return "this is b"
              };
          }
      
          public foo: Function & { bar?: () => string };
      }
      
      const obj = new MyClass();
      
      console.log(obj.foo());
      console.log(obj.foo.bar());
      

      Test it in the Typescript Playgroung

      这是可能的,因为通过在类方法的类型定义中使用& 符号,我们大惊小怪Function 类型和具有1 个属性的Object 的简单类型结合在一起bar 本身就是另一个函数。然而,这个属性必须在第二种类型中声明为可选,这是因为 Typescript 会抱怨在类的构造函数中的第一个赋值中它不满足这个额外的类型。但实际上构造函数中的下一个表达式通过分配foobar 属性来完成它。

      --- 旧答案如下,不要使用它,它不完全符合 OP 的要求

      为类成员'foo'定义一个getter访问器方法而不是方法,该方法返回一个带有函数'bar'的对象

          class MyClass {
              get foo(): {bar: (text: string) => string} {
      
                  // run any code here that needs to be run before bar()
      
                  return {
                      bar: (text: string) => {
                          return text + ' FOO!';
                      }
                  }
              }
          }
      
          const obj = new MyClass();
          console.log(obj.foo.bar('thing'));
      

      【讨论】:

      • 看来foo() 不再是一个可调用的函数。有没有办法做类似的事情但仍然允许obj.foo() 工作?
      猜你喜欢
      • 2012-09-23
      • 2016-09-19
      • 2013-04-15
      • 2018-06-24
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      相关资源
      最近更新 更多