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

37

我厌倦了写这样的代码:

class Something {

    constructor() {

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

这是一个耗时的过程,而且很容易忘记绑定方法。我知道有类字段提案,但它仍处于第三阶段,似乎存在一些问题

我的当前解决方案(基于这个答案)看起来像这样:

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));

1
一个选择是,考虑到你只需要绑定在不同上下文中调用的方法(通常是事件处理程序),可以使用箭头函数来调用这些方法而不改变上下文。 - Randy Casburn
3
你为什么觉得你一定要做这件事呢? - user229044
4
这种 this.self = this 的方法怎么样?这里没有讨论到…… - Ryan
我不熟悉那种方法,@Ryan能否分享一些相关信息(最好是在回答中)? - Dominic P
1
@DominicP - 只是在提到这里讨论的老习语:https://dev59.com/wHNA5IYBdhLWcg3wcNXF。看起来这种方法已经不再必要或推荐了,所以算了 :). - Ryan
显示剩余4条评论
5个回答

33

在ES6中使用fat arrow函数(通常称为箭头函数)

anotherOne = ()=> {
...
}

这样调用 onClick={this.anotherOne}; 不需要在构造函数中绑定

来自于 ECMA 规范

箭头函数内部引用 arguments、super、this 或者 new.target 时,必须从词法上层环境找到相应的绑定。通常情况下,这个环境是最近一层函数的函数环境。


5
我不认为这个绑定方法将对象的方法绑定到其实例上。似乎没有回答所提出的问题。 - jfriend00
2
OP已经提到过这一点,并表示他不喜欢它带来的问题。@jfriend00?这是类字段语法,这意味着函数内部的this将绑定到实例而无需额外的.bind,对吗?虽然可能不是OP正在寻找的内容,因为他明确将其作为被丢弃的可能性提到了。 - CertainPerformance
@CertainPerformance - 嗯,这个答案没有展示足够的定义上下文,无法确定他们在做什么。对我来说显然不是很清楚。 - jfriend00
2
你似乎忘了提到这依赖于公共类字段功能。 - Felix Kling

19

虽然回答这个问题有些晚了,但是由于没有被接受的答案,我会尽力为后来者解答。

为了自动地将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)); 

我会明确地将这个作为答案+1。 - Mike Boutin

9
你也可以像这样使用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会在哪里呢?;) - Dominic P

4
您可以使用ES6箭头函数:
method = () => {
    //Do stuff
}

文档所述:
箭头函数表达式是常规函数表达式的语法紧凑替代品,但没有它自己的绑定到 this、arguments、super 或 new.target 关键字。

1
我不明白这个绑定方法如何将对象的方法绑定到其实例上。似乎并没有回答所问的问题。 - jfriend00
OP已经提到过这一点,并表示他不喜欢它带来的问题。 - CertainPerformance

3

类字段可以用于自动绑定到当前实例的函数。

class X {
  fn = () => console.log(this.constructor.name)
}
const x = new X;
x.fn(); // regular call
const fn = x.fn;
fn(); // this is preserved
const fn2 = x.fn.bind(null);
fn2(); // cannot rebind this


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接