Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot call a method within a class it defined it in ES6 in Node.js [duplicate]

I am making an app using Node.js, Express.js and MongoDB. I am using a MVC pattern and also have separate file for routes. I am trying me make a Controller class, in which a method calls another method declared within it. But I cannot seem to be able to do this. I get "Cannot read property '' of undefined".

index.js file

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

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

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

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

ProductController.js file

class ProductController {
  constructor(){}

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

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

When I run this I get following error message:

Cannot read property 'callme' of undefined

I have ran this code by itself with little modification as following and it works.

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

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

Why does one work and not the other? HELP!

like image 488
Kucl Stha Avatar asked Sep 21 '16 16:09

Kucl Stha


People also ask

How do you define a private method or variable inside a class in JavaScript or ES6?

To make a public method private, you prefix its name with a hash # . JavaScript allows you to define private methods for instance methods, static methods, and getter/setters. The following shows the syntax of defining a private instance method: class MyClass { #privateMethod() { //... } }

How do you call a class method in JavaScript?

Class methods are created with the same syntax as object methods. Use the keyword class to create a class. Always add a constructor() method. Then add any number of methods.

What are classes in ES6?

There are two types of Class in ES6: parent class/super class: The class extended to create new class are know as a parent class or super class. child/sub classes: The class are newly created are known as child or sub class. Sub class inherit all the properties from parent class except constructor.

What is Call () in JavaScript?

The call() method is a predefined JavaScript method. It can be used to invoke (call) a method with an owner object as an argument (parameter). With call() , an object can use a method belonging to another object.


2 Answers

When you pass create method as method it is probably called in different context (this) as you expect. You can bind it:

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

There are many other ways how to ensure this refers to correct object.

E.g. wrap it with function (either arrow or classical):

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

Or bind methods in constructor:

constructor() {
    this.create = this.create.bind(this);
}
like image 181
madox2 Avatar answered Oct 18 '22 18:10

madox2


Your method is being rebound to the Layer class within express, losing its original context. The way that express handles routes is by wrapping each one in a Layer class, which assigns the route callback to itself:

this.handle = fn;

That is where your problems arise, this assignment automatically rebinds the function context to Layer. Here is a simple example demonstrating the problem:

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

Others have already pointed out the solution, which is to explicitly bind your method to the ProductController instance:

app.post('/product', productController.create.bind(productController));
like image 35
Rob M. Avatar answered Oct 18 '22 20:10

Rob M.