class with static and instance methods - javascript

The goal - create function, that behaves like class with static methods and at the same time can be used to create instanses. All this resides in dedicated namespace. I am looking for "standard" ptototype approach. And is it good practice in general?
I tried to avoid use reference to "class" by function name. If I would have 10 or more static methods it could be difficult. In first example I used _self, and in another method defineProperties.
var MyNamespace = (function() {
function MyClass0(e) {
this.e = e;
var _self = MyClass0;
_self.prototype = {
instanceMethod: function() {
}
}
_self.staticMethod = function() {
}
if (!e) {
return _self;
}
}
function MyClass1(e) {
this.e = e;
MyClass1.prototype = {
instanceMethod: function() {
}
}
var properties = {
staticMethod: {
value: function() {
}
}
};
Object.defineProperties(MyClass1, properties)
if (!e) {
return MyClass1;
}
}
return {
MyClass0: MyClass0(), // use _self
MyClass1: MyClass1() // use defineProperties
}
})();

Related

Create object of namespace Javascript singleton class

I am new to IIFE and trying to implement namespace in JavaScript on a Siungleton JavaScript class:
I have a JavaScript class (say main class):
var myIIFE = (function () {
var x = null;
//constructor function
var myIIFE = function() {
var a = new IIFE.InsideIIFE(); //says not a constructor
}
myIIFE.prototype = {
//some methods
}
function createIstance() {
return new myIIFE();
}
return {
getInstance: function() {
if (!this.instance) {
this.instance = createInstance();
}
return this.instance;
}
};
})();
Then I have another JavaScript namespaced class:
myIIFE.InsideIIFE = (function() {
var inside = function() {}
inside.prototype = { //some methods }
return inside;
});
I want to create an object of myIIFE.InsideIIFE in myIIFE, and this is throwing me an error:
myIIFE.InsideIIFE is not a constructor
Am I doing something wrong? Or if this is a correct approach then what changes I should make.
I tried using new this.InsideIIFE() and just InsideIIFE, but non of them worked.
edit:
From my analysis, I understand that myIIFE (the parent) is an object as it return an object at:
return {
getInstance: function() {
//method body
}
}
There are many issues with this code, let's try to run it in our heads, from the start:
var myIIFE = (function () {
....
})();
This results in myIIFE being an object, to be precise, this object:
{
getInstance: function() {
if (!this.instance) {
this.instance = createInstance();
}
return this.instance;
}
}
So, then I assume, you do
myIIFE.getInstance()
Which tries to return new myIIFE();
It runs into myIIFE() and tries to do this:
new IIFE.InsideIIFE();
I assume you meant to write
new myIIFE.InsideIIFE();
because IIFE is not defined it anywhere in the code you provided.
Let's see what is myIIFE.insideIIFE
var myIIFE.InsideIIFE = (function() {
var inside = function() {}
inside.prototype = { //some methods }
return inside;
});
First of all you start with var, which is wrong, because myIIFE is already defined and you are just adding a new property to it. so it should be simply
myIIFE.InsideIIFE = (function() {
var inside = function() {}
inside.prototype = { //some methods }
return inside;
});
and it should work.
In general, it seems by your code, like you have not grasped the whole "constructor function" concept very well. I would suggest you take look at the following links, they should help.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript

Javascript inheritance and function overriding

// Base state class -------------------------
function StateConstuctor()
{
}
// Inherited learn class --------------------
function StateLearnConstructor()
{
}
// Inherited exam class ---------------------
function StateExamConstructor()
{
}
function extend(Child, Parent)
{
var F = function() { }
F.prototype = Parent.prototype
Child.prototype = new F()
Child.prototype.constructor = Child
Child.superclass = Parent.prototype
}
function createState(rollType)
{
if (rollType == 'learn')
{
extend(StateLearnConstructor, StateConstuctor);
var state = new StateLearnConstructor();
return state;
}
else if (rollType == 'exam')
{
extend(StateExamConstructor, StateConstuctor);
var state = new StateExamConstructor();
return state;
}
}
StateConstuctor.prototype.getTitles = function()
{
console.log('base "virtual" function');
}
StateLearnConstructor.prototype.getTitles = function()
{
console.log('learn');
}
StateExamConstructor.prototype.getTitles = function()
{
console.log('exam');
}
Hello, I have the following "OOP" structure and I want to emulate something like virtual functions in C++. So I have base virtual function in StateConstructor and different realizations for each subclass.
var state = createState('exam');
state.getTitles();
But this code calls StateConstructor base virtual function. What's wrong here?
createState() is overwriting the prototypes for your StateLearnConstructor and your StateExamConstructor after you have assigned functions to them.
You shouldn't be conditionally extending them. Just extend them:
extend(StateLearnConstructor, StateConstuctor);
extend(StateExamConstructor, StateConstuctor);
StateConstuctor.prototype.getTitles = function () {
console.log('base "virtual" function');
};
StateLearnConstructor.prototype.getTitles = function () {
console.log('learn');
};
StateExamConstructor.prototype.getTitles = function () {
console.log('exam');
};
function createState(rollType) {
if (rollType == 'learn') {
return new StateLearnConstructor();
} else if (rollType == 'exam') {
return new StateExamConstructor();
}
}
Once you do that, your "virtual functions" should work as expected.
demo
Note: Your implementation for extend() is more complicated than it needs to be. The modern way to inherit a prototype is to use Object.create():
function extend(Child, Parent) {
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.superclass = Parent.prototype;
}

Using "dot" inside a prototype name in JavaScript

Lets say I have this class:
function classA(n){
this.name = n
}
classA.prototype.getName = function(){
return this.name
}
var x = new classA('john')
console.log(x.getName())
My question is: can I group multiple methods inside a namespace? So I would like to do that:
var x = new classA('john')
console.log(x.CONSTANT.getName())
So I would like to call some methods as x.someMethod() and others as x.CONSTANT.otherMethod()
PS: I'm looking for a cross-browser method. Bind is not working in Safari and IE9.
You can do it, for example, via bind. Google es5 shim for implementation of bind in browsers, which don't support it natively.
function MyClass(name) {
this.name = name;
this.CONSTANT.otherMethod = this.CONSTANT.otherMethod.bind(this);
}
MyClass.prototype.CONSTANT = {
otherMethod: function() {
alert(this.name);
}
};
As far as I know a constant is just a property and it can't contain methods, you need to separate your objects and use methods to have the same effect:
function A (id) {
this.id = id;
this.showId = function () { return this.id; }
};
function B (a) {
this.a = a;
this.getA = function () { return this.a; }
}
var a = new A(12);
var b = new B(a);
b.getA().showId();
edit:
You can use a literal object as follow
function B (id) {
this.id = id;
this.CONSTANT = { otherMethod: function () { alert("..."); } };
someMethod = function () { return this.id; }
}
but the literal CONSTANT object can't access B-object methods,
Consider the #kirilloid post to round this.
You can, but you have to be careful because it won't act like you think it will. The this for the method will be the namespace, not the root object.
For example, in x.CONSTANT.getName(), the this object will be x.CONSTANT, and not x.
Here's some sample code which kinda does what you ask (or in jsfiddle):
function MyClass() {}
MyClass.prototype.CONSTANT = {
getName: function() {
alert('Foo');
}
};
var c = new MyClass();
c.CONSTANT.getName();
To make sure the this is right, you need to do much more.
You can use getters/setters (read this article) to achieve this. For example you may define it like this:
classA.prototype.__defineGetter__('CONSTANT', function() {
var that = this;
return {
getName: function() {
return that.name;
}
};
});
Note that holding reference to the object. It will work now
x = new classA('test');
x.CONSTANT.getName();
// result - test

Unable to access members in javascript

I'm defining a class in javascript as
Class = (function() {
var privateFunction = function() { return "private"; }
return { publicFunction: function() { return privateFunction("public"); } };
)();
Here user can access Class.publicFunction, but not Class.privateFunction.
Now I want to provide the user an interface to extend this Class. So I added a public function extend.
Class = (function() {
var privateFunction = function() { return "private"; }
return {
publicFunction: function() { return privateFunction("public"); }
extend: function(source) {
dest=this;
for(var prop in source)dest[prop] = source[prop]
}
};
)();
My aim was to use the extend attribute as follows
Class.extend({
someFunc: function() { return privateFunction("hooray"); }
});
and access it as
Class.someFunc()
The problem I face is the call to the privateFunction() in the extended function someFunc is not available for it. I can understand that it is the problem of the scope, but, is there anyway to solve my need.
While it's a horrible violation of encapsulation, you could do what you describe by passing the function you want to add as a string and evaling it in extend:
Class.extend({
someFunc: 'function() { return privateFunction("hooray"); }'
});
and in the extend function, change
for(var prop in source)dest[prop] = source[prop]
to
for(var prop in source)dest[prop] = eval(source[prop])
this.before = function(){return "public"};
this.publicFucntion = function(){privateFunction(this.before());}
Then just override this.before.

JavaScript Namespace Declaration

I created a javascript class as follow:
var MyClass = (function() {
function myprivate(param) {
console.log(param);
}
return {
MyPublic : function(param) {
myprivate(param);
}
};
})();
MyClass.MyPublic("hello");
The code above is working, but my question is, how if I want to introduce namespace to that class.
Basically I want to be able to call the class like this:
Namespace.MyClass.MyPublic("Hello World");
If I added Namespace.MyClass, it'll throw error "Syntax Error".
I did try to add "window.Namespace = {}" and it doesn't work either.
Thanks.. :)
Usually I'd recommend doing this (assuming Namespace is not defined elsewhere):
var Namespace = {};
Namespace.MyClass = (function () {
// ...
}());
A more flexible, but more complex, approach:
var Namespace = (function (Namespace) {
Namespace.MyClass = function() {
var privateMember = "private";
function myPrivateMethod(param) {
alert(param || privateMember);
};
MyClass.MyPublicMember = "public";
MyClass.MyPublicMethod = function (param) {
myPrivateMethod(param);
};
}
return Namespace
}(Namespace || {}));
This builds Namespace.MyClass as above, but doesn't rely on Namespace already existing. It will declare and create it if it does not already exist. This also lets you load multiple members of Namespace in parallel in different files, loading order will not matter.
For more: http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth
YUI has a nice method for declaring namespaces
if (!YAHOO) {
var YAHOO = {};
}
YAHOO.namespace = function () {
var a = arguments,
o = null,
i, j, d;
for (i = 0; i < a.length; i = i + 1) {
d = ("" + a[i]).split(".");
o = YAHOO;
for (j = (d[0] == "YAHOO") ? 1 : 0; j < d.length; j = j + 1) {
o[d[j]] = o[d[j]] || {};
o = o[d[j]];
}
}
return o;
}
Place it above any function that you want to namespace like this:
YAHOO.namespace("MyNamespace.UI.Controls")
MyNamespace.UI.Controls.MyClass = function(){};
MyNamespace.UI.Controls.MyClass.prototype.someFunction = function(){};
This method is actually stand-alone and can easily be adapted to your application. Just find and replace "YAHOO" with your application's base namespace and you'll have something like MyOrg.namespace. The nice thing with this method is that you can declare namespaces at any depth without having to create object arrays in between, like for "UI" or "Controls"
A succinct way to do what you're asking is create "Namespace" as an object literal like this:
var Namespace = {
MyClass : (function() {
... rest of your module
})();
};
This could cause conflicts if you wanted to attach other details to Namespace in other files, but you could get around that by always creating Namespace first, then setting members explicitly.
Checkout the namespace library, it is very lightweight and easy to implement.
(function(){
namespace("MyClass", MyPublic);
function MyPublic(x){
return x+1;
}
})();
It supports automatically nesting as well
namespace("MyClass.SubClass.LowerClass", ....)
Would generate the necessary object hierarchy, if MyClass, SubClass did not already exist.
bob.js has nice syntax to define JavaScript namespace:
bob.ns.setNs('myApp.myMethods', {
method1: function() {
console.log('This is method 1');
},
method2: function() {
console.log('This is method 2');
}
});
//call method1.
myApp.myMethods.method1();
//call method2.
myApp.myMethods.method2();
(function($){
var Namespace =
{
Register : function(_Name)
{
var chk = false;
var cob = "";
var spc = _Name.split(".");
for(var i = 0; i<spc.length; i++)
{
if(cob!=""){cob+=".";}
cob+=spc[i];
chk = this.Exists(cob);
if(!chk){this.Create(cob);}
}
if(chk){ throw "Namespace: " + _Name + " is already defined."; }
},
Create : function(_Src)
{
eval("window." + _Src + " = new Object();");
},
Exists : function(_Src)
{
eval("var NE = false; try{if(" + _Src + "){NE = true;}else{NE = false;}}catch(err){NE=false;}");
return NE;
}
}
Namespace.Register("Campus.UI.Popup")
Campus.UI.Popup=function(){
defaults={
action:'',
ispartialaction:'',
customcallback:'',
confirmaction:'',
controltoupdateid:'',
width:500,
title:'',
onsubmit:function(id){
var popupid=id+"_popupholder";
if(this.ispartialaction){
$.ajax({
url:this.action,
type:"Get",
context:this,
success:function(data){
$('#'+id).parents('body').find('form').append("<div id'"+popupid+"'></div>");
var ajaxContext=this;
$("#"+popupid).dialog({
autoopen:false,
model:true,
width:this.width,
title:this.title,
buttons:{
"Confirm":function(){
if(ajaxContext.customcallback==''){
var popupform=$(this).find("form");
if(popupform.isValid()){
$.post(ajaxContext.confirmaction,popupform.serialize(),function(d){
if(d!='')
{
$.each(d.Data,function(i,j){
switch(j.Operation)
{
case 1:
if($('#'+j.ControlClientID).is("select"))
{
$('#'+j.ControlClientID).val(j.Value);
$('#'+j.ControlClientID).change();
}
else if($('input[name="'+j.ControlClientID+'"]').length>0)
{
$('input[name="'+j.ControlClientID+'"][value="'+j.Value+'"]').prop("checked",true);
}
break;
case 2:
if($('#'+j.ControlClientID).is("select"))
{
$('#'+j.ControlClientID).append("<option selected='selected' value=\""+j.Value+"\">"+j.Text+"</option>");
}
else
{
var len=$('input[name="'+j.ControlClientID+'"]').length;
$('#'+j.ControlClientID+"list").append('<li><input type="checkbox" name="'+j.ControlClientID+'" value="'+j.Value+'" id="ae'+j.ControlClientID+len+'"/><label for "ae'+j.ControlClientID+len+'">'+j.Text+'</label>');
}
break;
case 0:
$('#'+j.ControlClientID).val(j.Value);
breakl
default:break;
}
});
popupform.parent().dialog("destroy").remove();
$("#"+ajaxContext.controltoupdateid).change();
}
});
}
}
else
{
executeByFunctionName(ajaxContext.customcallback,window,new Array());
}
},
"Cancel":function(){
$(this).dialog("close");
}
}
});
$("#"+popupid).dialog("open");
$("#"+popupid).empty().append(data);
},
error:function(e)
{
alert(e);
}
});
}
else
{
var frm=document.createElement("form");
frm.id="CampusForm";
frm.name="CampusForm";
frm.action=this.action;
frm.method="post";
var arr=$($("#"+id).closest("body").find("form")).serializeArray();
$.each(arr,function(i,j){
var hidd=document.createElement("input");
hidd.type="hidden";
hidd.name=j.name;
hidd.value=j.value;
frm.appendChild(hidd);});
document.appendChild(frm);
frm.submit();
}
}
},
clicksubmit=function(){
var opts=$(this).data("CampusPopup");
opts.onsubmit($(this).attr("id"));
return false;
};
return
{
init:function(opt){
var opts=$.extend({},defaults,opt||{});
$(this).data('CampusPopup',opts);
$(this).bind("click",clicksubmit);
}};
}();
$.extend({CampusPopup:Campus.UI.Popup.init});
})(jQuery)
Automating namespaces declaration in javascript is very simple as you can see:
var namespace = function(str, root) {
var chunks = str.split('.');
if(!root)
root = window;
var current = root;
for(var i = 0; i < chunks.length; i++) {
if (!current.hasOwnProperty(chunks[i]))
current[chunks[i]] = {};
current = current[chunks[i]];
}
return current;
};
// ----- USAGE ------
namespace('ivar.util.array');
ivar.util.array.foo = 'bar';
alert(ivar.util.array.foo);
namespace('string', ivar.util);
ivar.util.string.foo = 'baz';
alert(ivar.util.string.foo);
Try it out: http://jsfiddle.net/stamat/Kb5xY/
Blog post: http://stamat.wordpress.com/2013/04/12/javascript-elegant-namespace-declaration/
This is the design pattern I use which allows for nested namespaces as well as adding to the namespace later (even from a separate JS file) so you don't pollute the global namespace:
Example: JsFiddle
(function ($, MyObject, undefined) {
MyObject.publicFunction = function () {
console.log("public");
};
var privateFunction = function () {
console.log("private");
};
var privateNumber = 0;
MyObject.getNumber = function () {
this.publicFunction();
privateFunction();
privateNumber++;
console.log(privateNumber);
};
// Nested namespace
MyObject.nested = MyObject.nested || {};
MyObject.nested.test = function (text) {
console.log(text);
};
}(jQuery, window.MyObject = window.MyObject || {}));
// Try it
MyObject.getNumber();
MyObject.nested.test('Nested');
Here is how to add to MyObject from another JavaScript file:
(function ($, MyObject, undefined) {
MyObject.newFunction = function () {
console.log("Added");
};
}(jQuery, window.MyObject = window.MyObject || {}));
// Pass `jQuery` to prevent conflicts and `MyObject` so it can be added to, instead of overwritten
This resource helped me learn all about the different JS design patterns: http://addyosmani.com/resources/essentialjsdesignpatterns/book/
To create new JavaScript namespaces (like Math), I personally define the following base class that can be extended, but not instantiated:
class Namespace {
constructor() { throw TypeError("cannot instantiate a namespace") }
}
Subclasses will inherit constructor or override it with a method that calls super, so instantiation results in a TypeError either way.
The actual namespaces are defined by extending Namespace with any number of static properties (which can reference one another):
class ASCII extends Namespace {
static whitespace = "\t\n\r\v ";
static digits = "0123456789";
static uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static lowers = ASCII.uppers.toLowerCase();
static alphas = ASCII.uppers + ASCII.lowers;
static alphanumerics = ASCII.alphas + ASCII.digits;
}
const isDigital = candidate => ASCII.digits.includes(candidate);
The example use a bunch of string constants, but a namespace can contain any types of value, including functions (defined as static methods).

Categories