patternjavascriptModerate
Creating a class library in JavaScript
Viewed 0 times
creatingclasslibraryjavascript
Problem
I am an experienced Java programmer, but fairly new to JavaScript.
I am creating a chat library in JavaScript, and have it working, but want to know if I am going about this correctly, and following correct JavaScript coding standards.
I have a
To declare methods I am using
I have seen other libraries put methods on the class prototype
or declare them like,
Not sure which is best.
So my
Not sure if my code makes sense from a usage perspective, or if I'm missing anything else.
```
var SDK = {};
function Credentials() {
this.host = "";
this.app = "";
this.url = "";
this.applicationId = "";
}
/**
* Listener interface for a LiveChatConnection.
* This gives asynchronous notification when a channel receives a message, or notice.
*/
function LiveChatListener() {
/**
* A user message was received from the channel.
*/
this.message = function(message) {};
/**
* An informational message was received from the channel.
* Such as a new user joined, private request, etc.
*/
this.info = function(message) {};
/**
* An error message was received from the channel.
* This could be an access error, or message failure.
*/
this.error = function(message) {};
/**
* Notification that the connection was closed.
*/
this.closed = function() {};
/**
* The channels users changed (user joined, left, etc.)
* This contains a comma separated values (CSV) list of the current channel users.
* It can be passed to the SDKConne
I am creating a chat library in JavaScript, and have it working, but want to know if I am going about this correctly, and following correct JavaScript coding standards.
I have a
SDK object, LiveChatConnection class, and LiveChatListener and Credentials interfaces (I know classes and interfaces do not exist in JavaScript, but this seems to be how to create them).To declare methods I am using
this.myMethod = function() {...};I have seen other libraries put methods on the class prototype
MyClass.prototype.myMethod = function() {...}or declare them like,
myMethod : function() {...}Not sure which is best.
So my
SDK will be packaged in a sdk.js file on my website, then users should be able to import and use it in their web pages.Not sure if my code makes sense from a usage perspective, or if I'm missing anything else.
```
var SDK = {};
function Credentials() {
this.host = "";
this.app = "";
this.url = "";
this.applicationId = "";
}
/**
* Listener interface for a LiveChatConnection.
* This gives asynchronous notification when a channel receives a message, or notice.
*/
function LiveChatListener() {
/**
* A user message was received from the channel.
*/
this.message = function(message) {};
/**
* An informational message was received from the channel.
* Such as a new user joined, private request, etc.
*/
this.info = function(message) {};
/**
* An error message was received from the channel.
* This could be an access error, or message failure.
*/
this.error = function(message) {};
/**
* Notification that the connection was closed.
*/
this.closed = function() {};
/**
* The channels users changed (user joined, left, etc.)
* This contains a comma separated values (CSV) list of the current channel users.
* It can be passed to the SDKConne
Solution
I'm glad to see I'm not the only one who finds "interfaces" in JavaScript useful. Yes, the language doesn't support interfaces, but there is value in defining them anyway if you expect third party code to integrate with it.
Interfaces in JavaScript
There is no official vehicle for defining interfaces in JavaScript. That being said, when someone sees a constructor function, such as your
I know this is opinion, but I do like the .NET convention of prefixing an interface's name with a capital "I", which I think better communicates that this is an interface. So
On the other hand, if you intend on having people use the
I like your idea of just defining the interface, so I would stick with that.
Styles for Writing "Classes" in JavaScript
You are creating public methods and properties for your classes inside the constructor function. While this doesn't hurt anything, it does mean each instance of that class also has brand new instances of
These are my general style guidelines:
-
If all properties and methods are public, define the methods on the Prototype, and give the properties default values. This communicates what methods are available and what data the class uses.
-
If a class requires inheritance, then I use the
-
If a class requires "private" data, then I define methods and properties in the constructor function like you did:
Since you don't require inheritance or "private" data at this point, I would recommend style #1 as I find it easier to read, and less clutter in the code.
Namespaces in JavaScript
You have a global object called "SDK". It is a good idea to put your code in a "namespace" in JavaScript, but the classes you've written also appear to be global. I would recommend using an Immediately Invoked Function Expression (IIFE), which will give you a Function scope to define classes and variables that you only want internal to your l
Interfaces in JavaScript
There is no official vehicle for defining interfaces in JavaScript. That being said, when someone sees a constructor function, such as your
LiveChatListener function, they expect it to be an instantiable, usable class. I recommend using an Object Literal to define your interface, since it really is only for reference, and is not actually usable./**
* Listener interface for a LiveChatConnection.
* This gives asynchronous notification when a channel receives a message, or notice.
*/
var ILiveChatListener = {
/**
* A user message was received from the channel.
*/
message: function(message) {},
/**
* An informational message was received from the channel.
* Such as a new user joined, private request, etc.
*/
info: function(message) {},
/**
* An error message was received from the channel.
* This could be an access error, or message failure.
*/
error: function(message) {},
/**
* Notification that the connection was closed.
*/
closed: function() {},
/**
* The channels users changed (user joined, left, etc.)
* This contains a comma separated values (CSV) list of the current channel users.
* It can be passed to the SDKConnection.getUsers() API to obtain the UserConfig info for the users.
*/
updateUsers: function(usersCSV) {},
/**
* The channels users changed (user joined, left, etc.)
* This contains a HTML list of the current channel users.
* It can be inserted into an HTML document to display the users.
*/
updateUsersXML: function(usersXML) {}
};I know this is opinion, but I do like the .NET convention of prefixing an interface's name with a capital "I", which I think better communicates that this is an interface. So
LiveChatListener becomes ILiveChatListener.On the other hand, if you intend on having people use the
LiveChatListener as a base class of some sort, then what you've created is an abstract base class:function AbstractLiveChatListener() {
}
AbstractLiveChatListener.prototype = {
constructor: AbstractLiveChatListener,
message: function(message) {
throw new Error("Not Implemented");
},
info: function(message) {
throw new Error("Not Implemented");
},
...
};I like your idea of just defining the interface, so I would stick with that.
Styles for Writing "Classes" in JavaScript
You are creating public methods and properties for your classes inside the constructor function. While this doesn't hurt anything, it does mean each instance of that class also has brand new instances of
Function for each public method. The "best practice" is to define public methods on the Prototype, unless they need access to "private" data.These are my general style guidelines:
-
If all properties and methods are public, define the methods on the Prototype, and give the properties default values. This communicates what methods are available and what data the class uses.
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype = {
x: 0,
y: 0,
constructor: Point,
isAbove: function(p) {
return this.y > p.y;
},
isBelow: function(p) {
return this.y < p.y;
}
};-
If a class requires inheritance, then I use the
Foo.prototype.bar = function() style for defining methods on the Prototype:function Point3D(x, y, z) {
Point.call(this, x, y);
this.z = z;
}
Point3D.prototype = Object.create(Point.prototype);
Point3D.prototype.contains = function(p) {
return this.x >= p.x
&& this.y >= p.y
&& this.z >= p.z;
};-
If a class requires "private" data, then I define methods and properties in the constructor function like you did:
function Point(x, y) {
x = x || 0;
y = y || 0;
var
getX = function() {
return x;
},
getY = function() {
return y;
},
isAbove = function(p) {
return y > p.y;
},
isBelow = function(p) {
return y < p.y;
};
// Define public interface
this.getX = getX;
this.getY = getY;
this.isAbove = isAbove;
this.isBelow = isBelow;
}Since you don't require inheritance or "private" data at this point, I would recommend style #1 as I find it easier to read, and less clutter in the code.
Namespaces in JavaScript
You have a global object called "SDK". It is a good idea to put your code in a "namespace" in JavaScript, but the classes you've written also appear to be global. I would recommend using an Immediately Invoked Function Expression (IIFE), which will give you a Function scope to define classes and variables that you only want internal to your l
Code Snippets
/**
* Listener interface for a LiveChatConnection.
* This gives asynchronous notification when a channel receives a message, or notice.
*/
var ILiveChatListener = {
/**
* A user message was received from the channel.
*/
message: function(message) {},
/**
* An informational message was received from the channel.
* Such as a new user joined, private request, etc.
*/
info: function(message) {},
/**
* An error message was received from the channel.
* This could be an access error, or message failure.
*/
error: function(message) {},
/**
* Notification that the connection was closed.
*/
closed: function() {},
/**
* The channels users changed (user joined, left, etc.)
* This contains a comma separated values (CSV) list of the current channel users.
* It can be passed to the SDKConnection.getUsers() API to obtain the UserConfig info for the users.
*/
updateUsers: function(usersCSV) {},
/**
* The channels users changed (user joined, left, etc.)
* This contains a HTML list of the current channel users.
* It can be inserted into an HTML document to display the users.
*/
updateUsersXML: function(usersXML) {}
};function AbstractLiveChatListener() {
}
AbstractLiveChatListener.prototype = {
constructor: AbstractLiveChatListener,
message: function(message) {
throw new Error("Not Implemented");
},
info: function(message) {
throw new Error("Not Implemented");
},
...
};function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype = {
x: 0,
y: 0,
constructor: Point,
isAbove: function(p) {
return this.y > p.y;
},
isBelow: function(p) {
return this.y < p.y;
}
};function Point3D(x, y, z) {
Point.call(this, x, y);
this.z = z;
}
Point3D.prototype = Object.create(Point.prototype);
Point3D.prototype.contains = function(p) {
return this.x >= p.x
&& this.y >= p.y
&& this.z >= p.z;
};function Point(x, y) {
x = x || 0;
y = y || 0;
var
getX = function() {
return x;
},
getY = function() {
return y;
},
isAbove = function(p) {
return y > p.y;
},
isBelow = function(p) {
return y < p.y;
};
// Define public interface
this.getX = getX;
this.getY = getY;
this.isAbove = isAbove;
this.isBelow = isBelow;
}Context
StackExchange Code Review Q#62227, answer score: 10
Revisions (0)
No revisions yet.