自动绑定 JS 类方法的好方法是啥?

Posted

技术标签:

【中文标题】自动绑定 JS 类方法的好方法是啥?【英文标题】:What is a good way to automatically bind JS class methods?自动绑定 JS 类方法的好方法是什么? 【发布时间】:2019-10-23 12:06:56 【问题描述】:

我厌倦了写这样的代码:

class Something 

    constructor() 

        this.method = this.method.bind(this);
        this.anotherOne = this.anotherOne.bind(this);
        // ...
    

这很耗时,而且很容易忘记绑定方法。我知道类字段提案,但它仍处于第 3 阶段,似乎与 some issues 一起提供。

我当前的解决方案(基于this answer)如下所示:

class Something 

    constructor() 

        // Get all defined class methods
        const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(this));

        // Bind all methods
        methods
            .filter(method => (method !== 'constructor'))
            .forEach((method) =>  this[method] = this[method].bind(this); );
    

这似乎可行,但我想知道是否有更好的方法,或者这种方法是否存在我不知道的问题。

更新:为什么要这样做?

我遇到的问题是,如果我不在构造函数中绑定我的类函数,我必须记住稍后“正确”调用它们。例如:

const classInstance = new Something();

// This fails for a non-obvious reason
someAction()
    .then(classInstance.method);

// This works of course but looks like we created a useless function if you don't know better
someAction()
    .then(result => classInstance.method(result));

【问题讨论】:

我会将绑定提取到一个单独的函数中,而不是在构造函数中 一个选项,考虑到您只需要绑定在不同上下文(通常是事件处理程序)中调用的方法,是使用箭头函数来调用这些方法而不更改上下文。 你使用 ES5 而不是胖箭头语法有什么原因吗? 你为什么觉得你必须这样做? this.self = this 方法怎么样?这里不讨论了…… 【参考方案1】:

在ES6中使用fat arrow函数(一般称为箭头函数)

anotherOne = ()=> 
...

像这样调用onClick=this.anotherOne;无需在构造函数中绑定

来自ECMA spec

任何对参数、super、this 或 new.target 的引用 ArrowFunction 必须解析为词法封闭中的绑定 环境。通常这将是一个函数环境 立即封闭函数。

【讨论】:

我根本看不到如何将对象的方法绑定到它的实例。似乎没有回答所提出的问题。 OP 已经提到了这一点,并表示他不喜欢随之而来的问题。 @jfriend00 ?这是类字段语法,这意味着函数内部的this 将绑定到实例而没有额外的.bind,对吗?可能不是 OP 想要的,因为他明确提到它是一种被丢弃的可能性,尽管 @CertainPerformance - 好吧,答案没有显示足够的定义上下文来知道他们在做什么。当然对我来说并不明显。 您似乎没有提到这依赖于 public class fields 功能。【参考方案2】:

你也可以像这样使用auto-bind

来自用法示例:

const autoBind = require('auto-bind');

class Unicorn 
    constructor(name) 
        this.name = name;
        autoBind(this);
    

    message() 
        return `$this.name is awesome!`;
    


const unicorn = new Unicorn('Rainbow');

// Grab the method off the class instance
const message = unicorn.message;

// Still bound to the class instance
message();
//=> 'Rainbow is awesome!'

// Without `autoBind(this)`, the above would have resulted in
message();
//=> Error: Cannot read property 'name' of undefined

【讨论】:

感谢分享。如果没有 sindresorhus,JS 会在哪里? ;)【参考方案3】:

你可以使用 ES6 箭头函数:

method = () => 
    //Do stuff

如docs中所述:

箭头函数表达式是正则函数表达式的语法紧凑替代方案,尽管它自己没有绑定到 this、arguments、super 或 new.target 关键字。

【讨论】:

我根本看不到如何将对象的方法绑定到它的实例。似乎没有回答所提出的问题。 OP 已经提到了这一点,并表示他不喜欢随之而来的问题。【参考方案4】:

回答这个问题似乎有点晚了,但没有公认的答案,所以我会尽我最大的努力让人们在我之后来到这里。

要为所有方法自动绑定this,可以使用“箭头”函数:

class Something 
  constructor() 
    // Don't need to bind `this`
  

  doSomething = () => 
    console.log(this); // `this` will be pointed to the instance
  


const s = new Something();
s.doSomething(); // => `this` is pointed to `s`

注意:请确保扩展此类Something 的类不会在其constructor 中使用doSomething 方法(例如:super.doSomething()),否则会报错。

回答更新后的问题:如果您不使用.call().apply() 手动绑定this,则this 的值取决于该方法的调用方式

例如:

class Something 
  constructor() 
    // didn't bind `this` here
  

  doSomething() 
    console.log(this);
  


const s = new Something();
const funcA = s.doSomething;
const object = 
  funcB: s.doSomething,
;

// these ways of calling `.doSomething()` result in different values of `this`:
funcA(); // => `this` is pointed to the global variable (`window` in browser environment)
window.funcA(); // => same as `funcA()`
s.doSomething(); // => `this` is pointed to `s`
object.funcB(); // =? `this` is pointed to `object`

除此之外,.then() 方法的实现类似于这样:

class Promise 
  // ...
  then(callback) 
    // code...
    callback(); // notice how the `callback` is called - not as a method of an object
    // code...
  

在您的代码示例中,将回调传递给.then() 方法的方式将影响回调中this 的值:

const classInstance = new Something();

// `this` inside `classInstance.method` is pointed to `this` inside the
// `.then` method, not the `classInstance`, as `classInstance.method()`
// will be called as `callback()`
someAction()
    .then(classInstance.method);

// `this` inside `classInstance.method` is pointed to `classInstance`,
// as the way the anonymous "arrow" function is called does not affect the way
// `classInstance.method` is called, so `this`'s value is controlled by the way
// you call it inside the callback (anonymous "arrow" function) of `.then()`
// method.
someAction()
    .then(result => classInstance.method(result)); 

【讨论】:

以上是关于自动绑定 JS 类方法的好方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

将 SwiftUI 警报或操作表绑定到值类型模型属性的可选性(当属性为 nil 时显示视图)的好方法是啥?

测试自定义视图的 onMeasure/onLayout/onDraw 方法的好方法是啥?

使用 spray-json 处理默认值的好方法是啥

在 C++ 中声明实例变量而不构造它们的好方法是啥? [关闭]

通过 radius API 或 Node.js 模块在世界范围内搜索 zip 的好方法是啥?

在 JavaScript 中扩展 Error 的好方法是啥?