绑定与箭头函数(在 JavaScript 中,或用于响应 onClick)

IT技术 javascript reactjs arrow-functions
2021-03-30 06:01:20

所以我正在尝试学习JavaScript和/或在构造函数中react有点混淆.bind(this)但是,我想现在理解它,只是想知道,
为什么有人会在 中使用绑定与箭头函数JavaScript
(或在onClick事件中)。

使用一个和另一个有什么优点/缺点吗?

请参阅下面的代码示例。

绑定方法确保thisclickEvent函数中引用类:

class Click extends react.Component {
  constructor(props) {
    super(props)
    this.clickEvent = this.clickEvent.bind(this);
  }

  render = () => (
    <button onClick= { this.clickEvent } > Click Me < /button>
  )

  clickEvent() { console.log(this) } // 'this' refers to the class
}

但是,下面的方法也引用了该类:

class Click extends react.Component {

  render = () => (
    <button onClick= {() => { this.clickEvent() }}> Click Me < /button>
  )

  clickEvent() { console.log(this) } // 'this' refers to the class
}
2个回答

首先,让我们从每种技术的示例开始!
但这种差异更多地与JavaScript语言本身有关。

捆绑:

import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.clickHandler = this.clickHandler.bind(this);
  }

  clickHandler() {
    console.log( this )
  }

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>
  }
}

箭头功能:

import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
  }

  clickHandler = () => {
    console.log( this )
  }

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>
  }
}

优点和缺点:

在 public-class-field 上使用 Arrow-function 更易于人类阅读,因为代码行更少,但请记住,使用 Arrow-function 会影响两件事:

首先是 内存和性能当您使用类字段定义函数时,您的整个方法驻留在 类的每个实例上, 而不是在原型上,但是使用 绑定技术,callback每个实例上存储一小部分,它调用存储在原型。

可能受到影响的第二件事是您如何编写单元测试您将无法使用组件原型来存根如下函数调用:

const spy = jest.spyOn(MyComponent.prototype, 'clickHandler');
// ...
expect(spy).toHaveBeenCalled();

必须找到另一种方法来存根该方法,例如,通过 传入 spyprops 或 检查state更改

结论

计算机非常擅长阅读代码;你不应该担心这个。您可能需要考虑通过使用类属性箭头函数使您的代码更具人类可读性。


其他工具:

如果您想保持人类可读性和性能,请考虑使用plugin-transform-arrow-functions插件(尽管对我v7.2.0 造成了问题),只需运行npm i --save-dev @babel/plugin-transform-arrow-functions并将其添加到您的“ babel.config.js”或“ .babelrc”文件中,例如:

{
  "presets": ["module:metro-react-native-babel-preset"],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": false }],
    ["@babel/plugin-transform-arrow-functions", { "spec": true }]
  ]
}

或者你可以使用类似auto-bind 装饰器的东西,它会将上面的例子变成:

import React from 'react';

import { boundMethod as bind } from 'autobind-decorator';

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
  }

  @bind
  clickHandler() {
    console.log( this )
  }

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>
  }
}

请注意,没有必要将@bind每个功能都放在上面。你只需要绑定你传递的函数。例如onClick={this.doSomething}fetch.then(this.handleDone)

您的第二个示例在每个render. 首先,您只需创建一次绑定函数。

您可以在构造函数中将处理程序创建为箭头函数:

class Click extends react.Component {
  constructor(props) {
    super(props)
    this.clickEvent = () => {   // ***
      console.log(this);        // ***
    };                          // ***
  }

  render = () => (
    <button onClick={this.clickEvent}>Click Me</button>
  )
}

使用类字段建议语法(在大多数 React 项目的转译器设置中启用,并且您将其用于您的render函数),您可以这样编写:

class Click extends react.Component {
  constructor(props) {
    super(props)
  }

  clickEvent = () => {          // ***
    console.log(this);          // ***
  };                            // ***

  render = () => (
    <button onClick={this.clickEvent}>Click Me</button>
  )
}

这是同一件事。


旁注:您正在render为类的每个实例创建一个单独的函数。没有必要这样做,它可以在原型上。所以:

class Click extends react.Component {
  constructor(props) {
    super(props)
  }

  clickEvent = () => {
    console.log(this);
  };

  render() {
    return <button onClick={this.clickEvent}>Click Me</button>;
  }
}