【问题标题】:How to use arrow functions (public class fields) as class methods?如何使用箭头函数(公共类字段)作为类方法?
【发布时间】:2018-06-03 23:29:52
【问题描述】:

我是使用 ES6 类和 React 的新手,以前我一直将我的方法绑定到当前对象(在第一个示例中显示),但是 ES6 是否允许我将类函数永久绑定到带有箭头的类实例? (在作为回调函数传递时很有用。)当我尝试像使用 CoffeeScript 一样使用它们时遇到错误:

class SomeClass extends React.Component {

  // Instead of this
  constructor(){
    this.handleInputChange = this.handleInputChange.bind(this)
  }

  // Can I somehow do this? Am i just getting the syntax wrong?
  handleInputChange (val) => {
    console.log('selectionMade: ', val);
  }

因此,如果我将SomeClass.handleInputChange 传递给例如setTimeout,它将被限定为类实例,而不是window 对象。

【问题讨论】:

  • 我很想知道 TypeScript 的相同答案
  • TypeScript 的解决方案与 ES7 提案相同(参见accepted answer)。这由 TypeScript 原生支持。
  • 对于所有在这里结束的人来说,这是一个有趣的阅读主题"arrow functions in class properties might not be as great as we think"
  • 你应该避免在类中使用箭头函数,因为它们不是原型的一部分,因此不会被每个实例共享。这与给每个实例提供相同的函数副本相同。
  • @SourabhRanka 在构造函数中添加this 绑定也不行吗?

标签: javascript arrow-functions ecmascript-next class-fields


【解决方案1】:

您的语法略有偏差,只是在属性名称后缺少一个等号。

class SomeClass extends React.Component {
  handleInputChange = (val) => {
    console.log('selectionMade: ', val);
  }
}

这是一项实验性功能。您需要在 Babel 中启用实验性功能才能编译它。 Here 是一个启用了实验的演示。

要在 babel 中使用实验性功能,您可以从 here 安装相关插件。对于此特定功能,您需要transform-class-properties plugin

{
  "plugins": [
    "transform-class-properties"
  ]
}

您可以阅读更多关于类字段和静态属性提案here


【讨论】:

  • (虽然我知道这在 ES6 类之外有效)这似乎对我不起作用,babel 在=handleInputChange = 的第一个= 处抛出了一个意外的标记箭头
  • 您应该提供一些解释,例如这是 ES7 提案的实验性功能。
  • 当前的规范草案在 9 月进行了更改,因此您不应该按照 Babel 的建议将其用于自动绑定。
  • 对于 Babel 6.3.13,您需要激活预设 'es2015' 和 'stage-1' 来编译它
  • 可以,但是方法是在构造函数中添加到实例中而不是添加到原型中,这是一个很大的区别。
【解决方案2】:

不,如果您想创建绑定的、特定于实例的方法,您必须在构造函数中执行此操作。但是,您可以为此使用箭头函数,而不是在原型方法上使用 .bind

class SomeClass extends React.Component {
  constructor() {
    super();
    this.handleInputChange = (val) => {
      console.log('selectionMade: ', val, this);
    };
    …
  }
}

有一个 proposal 可能允许您省略 constructor() 并直接将赋值放在具有相同功能的类范围中,但我不建议使用它,因为它是高度实验性的。

或者,您始终可以使用.bind,它允许您在原型上声明方法,然后将其绑定到构造函数中的实例。这种方法具有更大的灵活性,因为它允许从类外部修改方法。

class SomeClass extends React.Component {
  constructor() {
    super();
    this.handleInputChange = this.handleInputChange.bind(this);
    …
  }
  handleInputChange(val) {
    console.log('selectionMade: ', val, this);
  }
}

【讨论】:

    【解决方案3】:

    我知道这个问题已经得到了充分的回答,但我只需要做出一点贡献(对于那些不想使用实验性功能的人)。由于必须在构造函数中绑定多个函数绑定并使其看起来凌乱的问题,我想出了一个实用方法,一旦在构造函数中绑定和调用,就会自动为您完成所有必要的方法绑定。

    假设我有这个类和构造函数:

    //src/components/PetEditor.jsx
    import React from 'react';
    class PetEditor extends React.Component {
      
       constructor(props){
            super(props);
            this.state = props.currentPet || {tags:[], photoUrls: []};
         
            this.tagInput = null;
            this.htmlNode = null;
    
            this.removeTag = this.removeTag.bind(this);
            this.handleChange = this.handleChange.bind(this);
            this.modifyState = this.modifyState.bind(this);
            this.handleKeyUp = this.handleKeyUp.bind(this);
            this.addTag = this.addTag.bind(this);
            this.removeTag = this.removeTag.bind(this);
            this.savePet = this.savePet.bind(this);
            this.addPhotoInput = this.addPhotoInput.bind(this);
            this.handleSelect = this.handleSelect.bind(this);
            
        }
        // ... actual method declarations omitted
    }

    看起来很乱,不是吗? 现在我创建了这个实用方法

    //src/utils/index.js
    /**
     *  NB: to use this method, you need to bind it to the object instance calling it
     */
    export function bindMethodsToSelf(objClass, otherMethodsToIgnore=[]){
        const self = this;
        Object.getOwnPropertyNames(objClass.prototype)
            .forEach(method => {
                  //skip constructor, render and any overrides of lifecycle methods
                  if(method.startsWith('component') 
                     || method==='constructor' 
                     || method==='render') return;
                  //any other methods you don't want bound to self
                  if(otherMethodsToIgnore.indexOf(method)>-1) return;
                  //bind all other methods to class instance
                  self[method] = self[method].bind(self);
             });
    }

    我现在需要做的就是导入该实用程序,并向我的构造函数添加一个调用,我不再需要在构造函数中绑定每个新方法。 新的构造函数现在看起来很干净,像这样:

    //src/components/PetEditor.jsx
    import React from 'react';
    import { bindMethodsToSelf } from '../utils';
    class PetEditor extends React.Component {
        constructor(props){
            super(props);
            this.state = props.currentPet || {tags:[], photoUrls: []};
            this.tagInput = null;
            this.htmlNode = null;
            bindMethodsToSelf.bind(this)(PetEditor);
        }
        // ...
    }

    【讨论】:

    • 您的解决方案很好,但它并没有涵盖所有生命周期方法,除非您在第二个参数中声明它们。例如:shouldComponentUpdategetSnapshotBeforeUpdate
    • 您的想法类似于自动绑定,具有明显的性能下降。您只需要绑定您传递的函数。见medium.com/@charpeni/…
    【解决方案4】:

    您正在使用箭头函数并将其绑定在构造函数中。所以你在使用箭头函数时不需要做绑定

    class SomeClass extends React.Component {
      handleInputChange = (val) => {
        console.log('selectionMade: ', val);
      }
    }
    

    或者当你使用下面的普通函数时,你只需要在构造函数中绑定一个函数

    class SomeClass extends React.Component {
       constructor(props){
          super(props);
          this.handleInputChange = this.handleInputChange.bind(this);
       }
    
      handleInputChange(val){
        console.log('selectionMade: ', val);
      }
    }
    

    不推荐直接在render中绑定函数。它应该始终在构造函数中

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-14
      • 2019-08-04
      • 2018-08-27
      相关资源
      最近更新 更多