最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin4浏览0评论

I am building an application in Node using Hapi.JS.

I have a class for an authentication plugin that is giving me all sorts of problems. When I attempt to reference this from within a method on the class, I get an error saying that this is undefined. Why is this happening?

An excerpt:

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();

Elsewhere this is being called like:

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

Every time the register function is called, I receive this error:

TypeError: Cannot set property 'server' of undefined

Why on earth is my instance not working?

I am building an application in Node using Hapi.JS.

I have a class for an authentication plugin that is giving me all sorts of problems. When I attempt to reference this from within a method on the class, I get an error saying that this is undefined. Why is this happening?

An excerpt:

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();

Elsewhere this is being called like:

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

Every time the register function is called, I receive this error:

TypeError: Cannot set property 'server' of undefined

Why on earth is my instance not working?

Share Improve this question asked May 27, 2016 at 18:21 BrennanBrennan 5,7422 gold badges19 silver badges24 bronze badges 10
  • 2 Does console.log(this); output anything of interest? Try that log in the constructor as well just cause? – bradj Commented May 27, 2016 at 18:25
  • @AbdulAhmad: No it does not. – Bergi Commented May 27, 2016 at 18:25
  • Is oauth.register(server); the only place where you call this method? – Bergi Commented May 27, 2016 at 18:26
  • 2 You never should use a class and export a singleton "module object". Either export the class itself, or use an object literal instead. – Bergi Commented May 27, 2016 at 18:27
  • 1 @Brennan I tested your class on https://babeljs.io/ and it works just fine. It seems that there is a bug in your js engine or something like that... You should really try to debug by putting break-points if you have an IDE that supports it. – plalx Commented May 27, 2016 at 18:31
 |  Show 5 more ments

1 Answer 1

Reset to default 16

ES6 class with babel doesn't autobind this for you. This is a mon misconception since class was introduced. There are multiple ways to solve it.

  1. Use ES7. Babel has an experimental (as of this post) class-properties plugin.

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

How does this work? When you use arrow-functions along with the class-properties plugin, it gets transformed to something like the following, binding this as you expect when you use class syntax.

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. Bind your class properties in the constructor

    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. Use createClass from react, which does the binding for you. Note we're only using react for its class property binding magic. We are not creating react ponents.

    import React from 'react'
    
    const OAuth = React.createClass({
      register (server, err, next) {
        this.server = server
        this.registerRoutes()
      }
    
      registerRoutes () {}
    })
    
  3. Use only autoBind from react-class. Here we're making a react ponent using ES6+ class syntax just to use the autoBind method. We don't have to use ponentWillMount, render, etc, which are provided with react ponents.

    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. Roll your own class property binder. It's a nice exercise, basically the same as option 2, possibly less code as well.

    // 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. If you actually want to create react ponents, you can bine arrow-functions and property initializers to do something like

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

评论列表(0)

  1. 暂无评论