绑定与箭头函数(在 JavaScript 中,或用于对点击做出反应)

问题描述 投票:0回答:2

所以我正在尝试学习

JavaScript
和/或
react
并且在构造函数中对
.bind(this)
的理解有点混乱。不过,我现在想明白了,只想知道,
为什么有人会在
JavaScript
中使用绑定与箭头函数?
(或在
onClick
事件中)。

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

请参阅下面的代码示例。

绑定方法确保

this
clickEvent
函数引用类:

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
}
javascript reactjs arrow-functions
2个回答
67
投票

首先,让我们从每个技术的例子开始吧!
但是区别更多的是

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 更具人类可读性, 因为更少的代码行, 但请记住,使用箭头函数会影响两件事:

首先是内存和性能;当你使用一个类字段来定义一个函数时,你的整个方法驻留在类的每个实例而不是原型上,但是使用bind技术,只有一个小的

callback
存储在每个实例上,这调用存储在原型上的方法。

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

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

必须找到另一种方法来存根该方法,例如,通过

props
中传递虚拟回调,或者 检查
state
更改
.

结论

计算机真的很擅长阅读代码;你不应该为此担心。 您可能需要考虑使用类属性箭头函数使您的代码更易于阅读。


其他工具:

如果你想保持人类可读性和性能,考虑使用 plugin-transform-arrow-functions 插件,只需运行

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 }]
  ]
}

在哪里

"spec": true
要求插件自动转换箭头功能, 进入方法/函数绑定(在
prototype
中)。

此外,似乎说插件是not与react-native兼容(至少链接帖子中提到的版本)。

或者, 你可以使用像 auto-bind decorator 这样的东西,它会把上面的例子变成:

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)


34
投票

您的第二个示例在每个

render
上重新创建包装函数。首先,您只创建一次原型函数,并在构造函数中为每个实例只创建一次绑定函数。

作为替代方案,您可以在构造函数中将处理程序创建为箭头函数:

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

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

无论你这样做还是使用

bind
,这在很大程度上是一个风格问题。 (我使用
bind
所以函数在原型上所以我们可以模拟它用于测试等目的。)

使用 class fields proposal syntax(在大多数 React 项目的转译器设置中启用,并且您正在将其用于您的

render
函数),您可以这样写:

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

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

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

这是一回事。为关闭实例的每个实例创建一个单独的

clickEvent
函数。上面的两个例子做了完全相同的事情(在构造函数中调用
super()
之后创建函数并将其分配给实例),唯一的区别是语法。


旁注:您正在为类的每个实例创建一个单独的

render
函数。没有必要这样做,它可以在原型上。所以:

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

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

    render() {
        return <button onClick={this.clickEvent}>Click Me</button>;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.