Rahul Sharma
Rahul Sharma

Reputation: 337

I just started learning node.js and i implemented a demo code exhibiting event listeners. I got an error

I am getting this error in my code

TypeError: account.on() is not a function

Where did i go wrong?

Code

var events = require('events');

function Account() {

  this.balance = 0;

  events.EventEmitter.call(this);

  this.deposit = function(amount) {
    this.balance += amount;
    this.emit('balanceChanged');
  };

  this.withdraw = function(amount) {
    this.balance -= amount;
    this.emit('balanceChanged');
  };
}

Account.prototype._proto_ = events.EventEmitter.prototype;

function displayBalance() {
  console.log('Account balance : $%d', this.balance);
}

function checkOverdraw() {
  if (this.balance < 0) {
    console.log('Account overdrawn!!!');
  }
}

function checkgoal(acc, goal) {
  if (acc.balance > goal) {
    console.log('Goal Achieved!!!');
  }
}


var account = new Account();

account.on('balanceChanged', displayBalance);

account.on('balanceChanged', checkOverdraw);

account.on('balanceChanged', function() {
  checkgoal(this, 1000);
});


account.deposit(220);
account.deposit(320);
account.deposit(600);
account.withdraw(1200);

Upvotes: 0

Views: 50

Answers (1)

Rob Raisch
Rob Raisch

Reputation: 17357

Your example code is not idiomatic Node JS.

I'd strongly recommend you follow the recommended best practices when creating new inheritable objects, as in:

var util=require('util');
var EventEmitter = require('events').EventEmitter;

var Account = function(){
  EventEmitter.call(this); // should be first
  this.balance=0; // instance var
};
util.inherits(Account,EventEmitter);

Account.prototype.deposit = function(amount){   
  this.balance += amount;
  this.emit('balanceChanged');
};

Account.prototype.withdraw = function(amount){  
  this.balance -= amount;
  this.emit('balanceChanged');
};

var account = new Account();

var displayBalance = function(){
  console.log("Account balance : $%d", this.balance);
};

account.on('balanceChanged',displayBalance);

account.deposit(200);
account.withdraw(40);

// ... etc. ....

Which, when run displays:

Account balance : $200
Account balance : $160

Best practices are there so that

  1. your code can be expressed in a way that is easy for others to understand
  2. you don't run into unexpected problems when you try to replicate functionality that is already defined, possibly complex and difficult to understand.

The reason that util.inherits exists is so you don't have to worry about how the prototype chain is constructed. By constructing it yourself, you will often run into the problem you experienced.

Also, since the current Node runtime (>6.0) also includes most of the ES6 spec, you can also (and really should) write your code as:

const util = require('util');
const EventEmitter = require('events').EventEmitter;

const Account = () => {
  EventEmitter.call(this);
  this.balance = 0;
};
util.inherits(Account,EventEmitter);

Account.prototype.deposit = (val) => {
  this.balance += val;
  this.emit('balanceChanged');
};

Account.prototype.withdraw = (val) => {
  this.balance -= val;
  this.emit('balanceChanged');
};

The use of the const keyword assures the variables you create cannot be changed inadvertently or unexpectedly.

And the use of the "fat arrow" function definition idiom (() => {}) is more succinct and thus quicker to type, but also carries the added benefit that it preserves the value of this from the surrounding context so you never have to write something like:

Account.prototype.doSomething = function() {
  var self = this;

  doSomething(val, function(err,res){
    if(err) {
      throw err;
    }
    self.result=res;
  });
};

which, using the 'fat arrow' construct becomes:

Account.prototype.doSomething = () => {
  doSomething(val, (err,res) => {
    if(err) {
      throw err;
    }
    this.result=res; // where 'this' is the instance of Account
  });
};

The "fat arrow" idiom also allows you to do some things more succinctly like:

// return the result of a single operation
const add = (a,b) => a + b;

// return a single result object
const getSum = (a,b) => {{a:a,b:b,sum:a+b}};

Another way to create inheritable "classes" in ES6 is to use its class construction notation:

const EventEmitter = require('events');

class Account extends EventEmitter {

  constructor() {
    super();
    this._balance = 0; // start instance vars with an underscore
  }

  get balance() {     // and add a getter
    return this._balance;
  }

  deposit(amount) {
    this._balance += amount;
    this.emit('balanceChanged');
  }

  withdraw(amount) { 
    this._balance -= amount;
    this.emit('balanceChanged');
  }

}

It should be noted that both ways of constructing inheritable prototypal objects is really the same, except that the new class construction idiom adds syntactic "sugar" to bring the declaration more in-line with other languages that support more classical object orientation.

The ES6 extensions to node offer many other benefits worthy of study.

Upvotes: 1

Related Questions