For test-driven development, the test case always comes first. The Phone inherits the Node.js EventEmitter: a phone is a customized event emitter, only augmented with its own properties and methods.
A phone has its own name, can powerOn itself.
Since it’s an event emitter, it can emit ‘income’ event, and can set the handler by addListener as event emitter.
test.js
1234567891011121314151617181920
varEventEmitter=require('events').EventEmitter;//var Phone = require('./phone-es5');//var Phone = require('./phone-util');varPhone=require('./phone-es6');// all implementation should have same interfacevarp1=newPhone('p1');// class instantiation with parameterp1.powerOn();// invoke instance methodlconsole.log(p1instanceofPhone);// test ISA object of super classconsole.log(p1instanceofEventEmitter);console.log(p1);// print instance at run-timevarincomeListener=function(number){console.log('incomming call from: '+number);};p1.addListener('income',incomeListener);p1.income('+4478123456');// invoke instance methodPhone.vendor('xiaomi');// static/class method
In fact, javascript object is prototype-based, there isn’t class at all, everything in javascript is object.
There’s only Object Extension (extends an exists object to a new object), rather than Class Inheritance (create new subclass that inherits the parent class).
To implement Inheritance is to build/extend the appropriate prototype chain.
The prototype-based inheritance is more flexible, and it’s easy to emulate traditional textbook or java-like class or inheritance.
phone-es5.js
12345678910111213141516171819
varEventEmitter=require('events').EventEmitter;functionPhone(name){EventEmitter.call(this);// call parent's constructorthis.setMaxListeners(20);// customize with parent's methodthis.name=name;// self field};Phone.prototype=newEventEmitter();// same as: Phone.prototype = Object.create(EventEmitter.prototype);Phone.prototype.constructor=Phone;Phone.prototype.powerOn=function(){console.log('[Phone instance method] '+this.name+' is powered on.');};Phone.prototype.income=function(number){this.emit('income',number);};Phone.vendor=function(name){console.log('[Phone static method] vendor: '+name);}module.exports=Phone;
Using Node.js util.inherits
Node.js util module provides syntactical sugar for easily implemention of class-like inheritance.
phone-util.js
123456789101112131415161718
varEventEmitter=require('events').EventEmitter;varutil=require('util');functionPhone(name){EventEmitter.call(this);// call parent's constructorthis.setMaxListeners(20);// customize with parent's methodthis.name=name;// self field};util.inherits(Phone,EventEmitter);Phone.prototype.powerOn=function(){console.log('[Phone instance method] '+this.name+' is powered on.');};Phone.prototype.income=function(number){this.emit('income',number);};Phone.vendor=function(name){console.log('[Phone static method] vendor: '+name);}module.exports=Phone;
util.inherits levarages Object.create() of ES5, the inherits line equals to:
From node-v4.0, lots of ES6 features are implemented by default(shipping). Such as block scoping, collections, promises and Arrow Functions. And Class syntax is also fully supported. But, note that class is just another syntactical sugar:
JavaScript classes are introduced in ECMAScript 6 and are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance.
With class syntax, class/inheritance can be implemented in a more straightforward manner.
phone-es6.js
123456789101112131415161718192021
'use strict'varEventEmitter=require('events').EventEmitterclassPhoneextendsEventEmitter{constructor(name){super()this.setMaxListeners(20)this.name=name}powerOn(){console.log('[Phone instance method] '+this.name+' is powered on.')}income(number){this.emit('income',number)}staticvendor(name){console.log(`[Phonestaticmethod]vendor:${name}`)}}module.exports=Phone
Note: ‘use strict’ is required, and we use the template string syntax in static method (class method).
And class declaration (as well as let, const, and function in strict mode) cannot be hoisted, unlike var and function.
In summary, with ES6 support in Node.js v4.0, class inheritance has never been more easier. So, let’s embrace ES6.