Невозможно вызвать метод внутри класса, который он определил в ES6 в Node.js

Я делаю приложение, используя Node.js, Express.js и MongoDB. Я использую шаблон MVC, а также имею отдельный файл для маршрутов. Я пытаюсь создать класс контроллера, в котором метод вызывает другой метод, объявленный в нем. Но мне кажется, что я не могу этого сделать. Я получаю «Невозможно прочитать свойство неопределенного».

файл index.js

let express = require('express');
let app = express();

let productController = require('../controllers/ProductController');

app.post('/product', productController.create);

http.createServer(app).listen('3000');

Файл ProductController.js

class ProductController {
  constructor(){}

  create(){
   console.log('Checking if the following logs:');
   this.callme();
  }

 callme(){
  console.log('yes');
 }
}
module.exports = new ProductController();

Когда я запускаю это, я получаю следующее сообщение об ошибке:

Cannot read property 'callme' of undefined

Я запустил этот код сам по себе с небольшими изменениями, как показано ниже, и он работает.

class ProductController {
  constructor(){}
  create(){
    console.log('Checking if the following logs:');
    this.callme();
  }

  callme(){
    console.log('yes');
  }
}
let product = new ProductController();
product.create();

Почему одно работает, а другое нет? ПОМОЩЬ!


person Kucl Stha    schedule 21.09.2016    source источник
comment
Вы должны никогда не экспортировать экземпляр класса. Либо экспортируйте сам класс, либо используйте только объект.   -  person Bergi    schedule 21.09.2016
comment
Вы должны определить методы внутри своего класса, используя синтаксис инициализатора свойств (callme = () => {...} вместо этого callme() {...}). github.com/facebook/flow/issues/5874#issuecomment-369922816   -  person Derek Soike    schedule 10.01.2019


Ответы (2)


Ваш метод привязывается к классу Layer внутри экспресса, теряя свой первоначальный контекст. Способ, которым Express обрабатывает маршруты, состоит в том, чтобы обернуть каждый из них в класс Layer, который назначает обратный вызов маршрута самому себе:

this.handle = fn;

Вот где возникают ваши проблемы, это назначение автоматически перепривязывает контекст функции к Layer. Вот простой пример, демонстрирующий проблему:

function Example() { 
   this.message = "I have my own scope"; 
} 
Example.prototype.logThis = function() { 
   console.log(this); 
}

function ReassignedScope(logThisFn) { 
   this.message = "This is my scope now";
   // simulation of what is happening within Express's Layer
   this.logThis = logThisFn; 
}

let example = new Example()
let scopeProblem = new ReassignedScope(example.logThis);

scopeProblem.logThis(); // This is my scope now

Другие уже указали на решение, которое заключается в явной привязке вашего метода к экземпляру ProductController:

app.post('/product', productController.create.bind(productController));
person Rob M.    schedule 21.09.2016

Когда вы передаете метод create как метод, он, вероятно, вызывается в другом контексте (this), как вы ожидаете. Вы можете связать его:

app.post('/product', productController.create.bind(productController));

Есть много других способов убедиться, что this относится к правильному объекту.

Например. оберните его функцией (либо стрелкой, либо классической):

app.post('/product', (...args) => productController.create(...args));

Или связать методы в конструкторе:

constructor() {
    this.create = this.create.bind(this);
}
person madox2    schedule 21.09.2016