【问题标题】:How to automatically apply argument to class constructor?如何自动将参数应用于类构造函数?
【发布时间】:2023-04-05 06:36:01
【问题描述】:

我有一个 es6 类 User 和一个全局函数 map(),如下所示:

class  User {
  constructor(public name: string) {}
}

const map = <T, R>(project: (value: T) => R) => {}

而不是编写以下内容:

map((value) => new User(value))

我想(以某种方式)写一些类似的东西:

map(new User)

我不确定这是否可能。

【问题讨论】:

  • new 调用该类。在第二个中,每次调用都会获得相同的实例。
  • 我怎样才能通过构造函数?
  • @MateuszWitkowski 不完全重复,因为在那个问题中 Date 对象具有我所理解的静态构造函数,在我的情况下它是不同的。我想了解new User 在不传递参数的情况下如何工作
  • 所有这些都是为了节省十二个字符?

标签: javascript typescript ecmascript-6


【解决方案1】:

如果你想要一个通用的解决方案,并且不能修改类,你可以使用一个高阶函数:

function make(klass) {
    return (...params) => new klass(...params);
}

// this:
map((value) => new User(value));

// becomes this:
map(make(User));

【讨论】:

    【解决方案2】:

    这是 JS 中 oop 与 fp 辩论中众所周知的“问题”——类构造函数与函数构造函数

    由于 es6 类需要 new 操作符,所以不可能写出 map(new User) 之类的东西

    你需要一个通过函数调用创建实例的类构造函数的包装器。国际海事组织,@baboo's approach 是要走的路

    class MyClass {
      // ...
    
      static create(...args) {
        return new MyClass(...args)
      }
    }
    const a = new MyClass('hello', [])
    const b = MyClass.create('world', 123])
    

    你可以阅读更多关于newhere的问题。

    另外,结帐daggy - Library for creating tagged constructors

    【讨论】:

      【解决方案3】:

      您所描述的模式称为范围安全的构造函数。它可以通过重载构造函数来实现,因此它可以在没有new 关键字的情况下与一起使用。

      interface User {
        name: string;
      }
      
      interface UserConstructor {
        new (name: string): User;
        (name: string): User;
      }
      

      同样的技巧也用于ArrayDate 等全局对象。

      我们需要识别是否使用了new关键字:

      const User = function (this: User | void, name: string): User {
        if (!(this instanceof User)) {
          return new User(name);
        }
      
        this.name = name;
        return this;
      } as UserConstructor;
      

      你的班级刚刚变成new-agnostic。

      console.log(
        new User('Bob'),
        User('Alice'),
      );
      

      这使我们能够写作:

      ['Alice', 'Bob'].map(User); // $ExpectType User[]
      

      【讨论】:

        【解决方案4】:

        如果在没有 new 的情况下调用函数,则可以使用 new.target 添加检查,然后使用 new 调用 then 函数。

        function Person(name) {
            if (!new.target) return new Person(...arguments);
            this.name = name;
        }
        
        var names = ['Jane', 'Dan', 'Grace', 'Paul'],
            instances = names.map(Person);
        
        console.log(instances);

        【讨论】:

        • 这也适用于 es6 类吗? (如果我将 Person 转换为一个类,我会在运行时得到 Class constructor cannot be called without the new keyword
        • 不,因为你需要一个静态构造函数,它不起作用。
        【解决方案5】:

        您可以在您的类中创建一个静态函数,该函数接受值参数并返回一个新用户:

        class User {
          static createUser(value) {
            return new User(value)
          }
        }
        

        然后使用:

        map(User.createUser)
        

        【讨论】:

        • 为了允许子类化,我建议你做return new this(value); - 然后你必须User.createUser.bind(User)
        • 谢谢,看起来不错,但现在我想了解类构造函数是如何工作的
        • 支持子类化的好点。但是,将上下文绑定到函数很慢。一个很好的折衷方案是覆盖子类中的函数
        【解决方案6】:

        你不能直接做。如果您控制目标函数(即它不是股票map 函数),您可以让它采用构造函数而不是函数:

        class User { constructor(private id: number) { }}
        function map<TIn, T>(value: TIn, ctor: new (a: TIn) => T): T{
            return new ctor(value)
        }
        map(10, User)
        

        另一个更灵活的解决方案是使用辅助函数将构造函数转换为所需的函数,尽管它并不比原始版本短很多:

        class User { constructor(private id: number) { }}
        function ctor<TIn, T>(ctor: new (a: TIn) => T): (value: TIn) => T{
            return value => new ctor(value)
        }
        [10, 11].map(ctor(User));
        

        【讨论】:

        • 是的,我实际上可以通过编写自己的高阶地图函数来控制地图。我会试试你的版本
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-17
        相关资源
        最近更新 更多