为什么我的 ES6(使用 Babel)class 说 `this` 在实例方法中未定义?

Why does my ES6 (using Babel) class say `this` is undefined in an instance method?

我正在使用 Hapi.JS 在 Node 中构建应用程序。

我有一个 class 的身份验证插件,它给我带来了各种各样的问题。当我尝试从 class 的方法中引用 this 时,我收到一条错误消息,指出 this 未定义。为什么会这样?

节选:

class OAuth {

  constructor () {}

  register (server, err, next) {
    this.server = server;
    this.registerRoutes();
  }

  registerRoutes () {
    console.log(this.server.route);
    this.server.route([
      {
          method: 'POST',
          path: '/oauth/token',
          config: {
              auth: false,
              handler: function(request,reply){
                console.log("test");
                reply("test");
              }
            }
      },
      {
        method: 'GET',
        path: '/test',
        config: {
          auth: false,
          handler: function(request,reply){
            console.log("test");
            reply("test");
          }
        }
      }
    ]);
  }
}
module.exports = new OAuth();

其他地方这样称呼:

const oauth = require('./oauth');
oauth.register(server);

每次调用注册函数时,我都会收到此错误:

TypeError: Cannot set property 'server' of undefined

为什么我的实例不工作?

ES6 class with babel 不会为你自动绑定 this。自引入 class 以来,这是一个常见的误解。有多种方法可以解决。

  1. 使用 ES7。 Babel 有一个实验性的(截至此 post)class-properties 插件。

    class OAuth {
      constructor () {}
    
      register = (server, err, next) => {
        this.server = server
        this.registerRoutes()
      }
    
      registerRoutes = () => {}
    }  
    

这是如何工作的?当您将 arrow-functions 与 class-properties 插件一起使用时,它会转换为类似以下内容,并按照您使用 class 语法时的预期进行绑定。

var OAuth = function OAuth() {
  var _this = this;

  _classCallCheck(this, OAuth);

  this.register = function (server, err, next) {
    _this.server = server;
    _this.registerRoutes();
  };

  this.registerRoutes = function () {};
}
  1. 在构造函数中绑定您的 class 属性

    class OAuth {
      constructor () {
        // `this` is the OAuth instance in the constructor
        this.register = this.register.bind(this)
        this.registerRoutes = this.registerRoutes.bind(this)
      }
    
      register (server, err, next) {
        // `this` is the global object.. NOT! 
        // after binding in the constructor, it's the OAuth instance ^_^
        // provided you use `new` to initialize your instance
        this.server = server
        this.registerRoutes()
      }
    
      registerRoutes () {}
    }
    
  2. 使用 React 中的 createClass,它会为您进行绑定。请注意,我们仅将 React 用于其 class 属性 绑定魔法。我们不是在创建 React 组件。

    import React from 'react'
    
    const OAuth = React.createClass({
      register (server, err, next) {
        this.server = server
        this.registerRoutes()
      }
    
      registerRoutes () {}
    })
    
  3. 仅使用 react-class 中的 autoBind。这里我们使用 ES6+ class 语法制作一个 React 组件,只是为了使用 autoBind 方法。我们不必使用 componentWillMountrender 等,这些都是 React 组件提供的

    import { autoBind } from 'react-class'
    
    class OAuth extends React.Component {
      constructor(props) {
        super(props)
        autoBind(this)
      }
    
      register (server, err, next) {
        this.server = server
        this.registerRoutes()
      }
    
      registerRoutes () {}
    }
    
  4. 自己制作 class 属性 活页夹。这是一个很好的练习,与选项 2 基本相同,代码也可能更少。

    // call it in your constructor
    bindStuff(this, ['register', 'registerRoutes', 'etc'])
    
    // define it somewhere as
    function bindStuff (context, props) {
      props.forEach(prop => {
        context[prop] = context[prop].bind(context);
      })
    }
    
  5. 如果你真的想创建 React 组件,你可以结合 arrow-functions 和 属性 初始化器来做类似

    class OAuthComponent extends React.Component {
      whateverMethodYouWant = (event) => {
        this.setState({somePropertyYouCareAbout: true}) // this is bound
      }
    
      anotherMethod = () => {
        this.whateverMethodYouWant() // this is bound
      }
    }