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

时间:2016-05-27 18:21:15

标签: javascript node.js ecmascript-6 babeljs hapijs

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

我有一个认证插件类,它给了我各种各样的问题。当我尝试从类中的方法中引用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

为什么我的实例不起作用?

1 个答案:

答案 0 :(得分:12)

带有babel的ES6课程不会为您自动提问this。自class引入以来,这是一种常见的误解。有多种方法可以解决它。

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

    class OAuth {
      constructor () {}
    
      register = (server, err, next) => {
        this.server = server
        this.registerRoutes()
      }
    
      registerRoutes = () => {}
    }  
    
  2. 这是如何工作的?当您使用箭头函数以及类属性插件时,它会转换为类似下面的内容,并在您使用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 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。我们不会创建反应组件。

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

      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. 滚动您自己的类属性绑定器。这是一个很好的练习,基本上与选项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. 如果您确实想要创建反应组件,可以将箭头函数和属性初始值设定项组合起来执行类似

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