【问题标题】:Inline binding of external function to class method外部函数与类方法的内联绑定
【发布时间】:2018-06-01 20:44:19
【问题描述】:

假设我有一个返回如下函数的函数:

function createGreeter(logger) {
  return function greet(greeting) {
    logger.log(greeting + ', ' + this.name);
  }
}

还有一个班级

class Person {
  constructor(name) {this.name = name;}
}

如果我想给Person这个使用控制台作为记录器的类分配一个greet方法,我可以想到几种方法:

1.

class Person {
  constructor(name) {this.name = name;}

  greet(greeting) {
    return createGreeter(console).call(this, greeting);
  }
}

2.

class Person {
  constructor(name) {this.name = name;}
}

Person.prototype.greet = createGreeter(console);

不过,我觉得这两个都有点难看; 1) 创建一个本质上不必要的包装方法,它只是简单地绑定 this 并调用该函数,以及 2) 在类主体之外修改原型,在我看来这会使类 API 变得不那么清晰。

对于内联赋值和将外部函数绑定到类方法没有更清晰/更短的语法。我在想这样的事情:

class Person {
  constructor(name) {this.name = name;}

  greet: createGreeter(console)
}

...这类似于您在对象文字中分配函数的方式。但这显然行不通。是否有类似的东西(现在或即将推出)?

另外,如果返回的函数很大,我想知道在 1) 中返回闭包的内存消耗和/或性能方面。每次在 Person 对象上调用 greet 方法时,都会创建一个新的函数对象,即使我们总是希望将相同的参数 (console) 传递给它。所以另一种方法是在类定义之前声明const consoleGreeter = createGreeter(console),并将greet 实现为return consoleGreeter.call(this, greeting),但这值得吗?

【问题讨论】:

  • 你能创建一个 sn-p 来复制这个问题吗?它可以工作Person.prototype.greet = createGreeter(console); var p1 = new Person( "abc" ); p1.greet( "10" );
  • ??我知道它有效。我要求一种更清晰或更短的方法来实现相同的结果。 JS 有很多方便的速记,我希望我在这个案例中错过了一个。
  • 你提到 但这显然不起作用 Person.prototype.greet = createGreeter(console);。你能复制这个吗?
  • 你的第二种方法对我来说看起来不错。你发现了什么样的陷阱?
  • @gurvinder372 原型方法不是他所说的方法。它正在工作,它应该。但他只是简单地给出了一个示例代码,只是为了说明,这个代码不会起作用,他说。

标签: javascript ecmascript-6 es6-class ecmascript-next


【解决方案1】:
class Person {
  constructor(name) {this.name = name;}
}

Person.prototype.greet = createGreeter(console);

是这样做的正确方法,除非有其他问题(它会在 TypeScript 中出现问题)。

这也可以通过class fields 完成,这是第 3 阶段的提案,很可能会在 ES2018 中出现:

class Person {
  greet = createGreeter(console);
}

这是一个快捷方式

class Person {
  constructor() {
    this.greet = createGreeter(console);
  }
}

首先 sn-p 评估一次createGreeter(console) 并将方法分配给类原型。第二个 sn-p 每次实例化类时都会对其求值,并将方法分配给类实例,效率较低。

【讨论】:

  • 这很有趣,它实际上与我的“虚构”代码非常相似,但有趣的是,代码将在构造时而不是在声明时进行评估。我当然也可以在此处存储 const consoleGreeter = createGreeter(console); 以避免每次创建新函数对象的成本,但我绝对倾向于直接进行原型操作以避免这样做。
  • 另请参阅此相关答案,stackoverflow.com/a/46729995/3731501。该案例与您的案例非常接近,但在某种程度上特定于 TS,并且 OP 故意试图避免直接访问prototype
【解决方案2】:

好的,所以这是bind外部方法的替代方法class

class Person {
  constructor(name) {this.name = name;}
  get greet() { return createGreeter(console) }
}

刚刚检查,this 也可以正常工作。

【讨论】:

  • 谢谢,你和帕特里克显然同时回答了同样的问题。确实是聪明的解决方案。
【解决方案3】:

这将实现相同的效果 #1,因为每次访问方法时都会调用包装器。如果您可以提高可读性...

function createGreeter(logger) {
  return function greet(greeting) {
    logger.log(`${greeting}, ${this.name}`);
  };
}

class Person {
  constructor(name) {
    this.name = name;
  }

  get greet() {
    return createGreeter(console);
  }
}

let person = new Person('Patrick');

person.greet('Hello');

【讨论】:

  • 我不得不承认get的这种用法让我困惑了一段时间;让一个属性返回一个函数确实很聪明!而且它自动绑定到this 也很高兴知道。
  • 也感谢完整的、可运行的代码 sn-p,谢谢!
猜你喜欢
  • 1970-01-01
  • 2013-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 2014-04-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多