JavaScript "Class" Structure to avoid using x = new widget(); - javascript

I would like use the following syntax where the parameter is an ID of HTML element, very similar as to how you setup JWPlayer but I can't figure out how they did it. This is so I can make it as simple as possible for someone else to use.
myWidget("htmlTargetId");
I'm trying to avoid having to do:
myWidget = new MyWidget("htmlTargetId");
I know that I can create the first by doing:
var myWidget = function(target) {
// Do something here
}
myWidget("htmlTargetId");
I need to add methods and properties etc but I would like a "constructor" that will create elements in the "htmlTargetId". What would be the best way to do this?
I tried a few variations, this is the latest attempt:
var myWidget = (function () {
var _target = undefined;
// constructor
var widget = function (target) {
_target = target;
version = 12;
};
widget.prototype = {
constructor: widget,
doSomething: function () {
console.log("I will so something to", target);
}
};
return widget;
})();
// Try out the new code
myWidget("htmlTargetId");
console.log(myWidget.version);
myWidget.doSomething();
But this gives me "undefined" and "Uncaught TypeError: undefined is not a function" I assume this is because the return statement is returning a function rather than an object because I'm not using "new"?
// Trying to avoid having to do this
superWidget = new myWidget("htmlTargetId");
Many thanks!

If you want to have multiple Widget instances,
var myWidget = (function () {
// constructor
var Widget = function (target) {
this._target = target;
};
Widget.prototype = {
constructor: Widget,
version: 12,
doSomething: function () {
console.log("...", this._target);
}
};
return function init(target) {
return new Widget(target);
};
})();
var widget1 = myWidget("foo"),
widget2 = myWidget("bar");
console.log(widget1.version); // 12
widget1.doSomething(); // "..." "foo"
widget2.doSomething(); // "..." "bar"
However, if you only need one "instance", you don't need any constructor:
var myWidget = function (target) {
myWidget._target = target;
};
myWidget.version = 12;
myWidget.doSomething = function () {
console.log("...", myWidget._target);
}
myWidget("foo");
console.log(myWidget.version); // 12
myWidget.doSomething(); // "..." "foo"

Related

Execute "static" method of a parent of a constructor accessed from a constructor array in Javascript

Phew, even the question was hard to write. Here's the problem: I have a "game", more like a random simulator, which needs to choose a random action from an array of actions, like this one:
actions = [ Action1, Action2, Action3 ]
I have actions written as classes inheriting from the Action parent class:
function Action() {
this.targets = [];
this.used = [];
this.execute = function(player) {
doStuff();
return whatever;
};
}
//btw the below I've seen in a JS OOP tutorial but it doesn't work and I have to implement init() in every child action
Action.init = function(player) {
var a = new this.constructor();
return a.execute(player);
};
Action.checkRequirements = function() {
return true;
};
Action1.prototype = new Action();
Action1.prototype.constructor = Action1;
function Action1 {
this.execute = function(player) {
doStuff();
return whatever;
}
}
Action1.init = function(player) {
var a = new Action1();
return a.execute(player);
}
So what I'm doing to execute an action and get its results is var foo = actions.getRandomVal().init(); (getRandomVal is a simple custom script that returns a random value from the array) It works well, creates the object instance which properly inherits all properties and methods, executes the exec() method and returns its results... but now I have a checkRequirements() method which I want to implement in like 10% of the 100+ actions I wish to do, and I want it to simply be inherited from the Action class so that when it is not implemented in the child class it simply returns true and I don't have an idea how. If I do var a = actions.getRandomVal(); and then a.checkRequirements(); it throws an exception that a.checkRequirements is not a function.
PS: this is a relatively small non-profit project for a (large) group of friends, I don't need it to work in every browser, it needs to work in Chrome and I can just tell them to use Chrome for it.
Since you only need to work with Chrome, I'd suggest to use ES6 class syntax which does all the inheritance properly, without the chance to mess up. This includes your Action1 constructor to inherit properties ("static class members") from the Action constructor as you'd expect.
class Action {
constructor() {
this.targets = [];
this.used = [];
}
execute(player) {
doStuff();
return whatever;
}
static init(player) {
var a = new this(); // no .constructor
return a.execute(player);
}
static checkRequirements() {
return true;
}
}
class Action1 {
execute(player) {
doOtherStuff();
return whateverelse;
}
}
It looks to me like you're calling checkRequirements() on an instance:
a.checkRequirements();
But it's implemented statically:
Action.checkRequirements = function() {
return true;
};
You probably want to bind this function to the prototype, so change the code above to this:
Action.prototype.checkRequirements = function() {
return true;
};
Then when you want to override this in a derived type, like Action1, you can do this:
Action1.prototype.checkRequirements = function () {
return (whatever);
}
As per comments, my guess is you want something like this...
// base Action type providing basic implementation
// Wrapped in an IIFE to prevent global scope pollution
// All functions are prototype bound to allow prototypical inheritance.
var Action = (function () {
function Action() {
this.targets = [];
this.used = [];
};
Action.prototype.doStuff = function () {
return;
}
Action.prototype.execute = function (player) {
this.doStuff();
return "whatever";
}
Action.prototype.checkRequirements = function () {
return "foo";
}
return Action;
})();
var Action1 = (function () {
Action1.prototype = new Action();
Action1.prototype.constructor = Action1;
function Action1() {
}
Action1.prototype.checkRequirements = function () {
// Super call
return Action.prototype.checkRequirements.call(this);
}
return Action1;
})();
var Action2 = (function () {
Action2.prototype = new Action();
Action2.prototype.constructor = Action2;
function Action2() {
}
Action2.prototype.checkRequirements = function () {
return "bar";
}
return Action2;
})();
// Set up array.
var array = [Action1, Action2];
// Create instances (this is where you would pick at random)
var a1 = new array[0]();
var a2 = new array[1]();
// var aofn = new array[rnd]();
// Tests
alert(a1.checkRequirements()); // Should "foo" because it called super (Action).
alert(a2.checkRequirements()); // Should "bar" because it's overridden.
Check it out on TypeScript Playground

UnCaught TypeError - Nested objects in Javascript? Why is this not allowed? Object literal notation works

Playing around with some JS tests and I'm trying to instantiate some nested objects in my v namespace. As you'll see below, ClassA and ClassB work as expected. When I try and nest some objects under another property (myCustomProperty) I start running into issues! Could someone explain?
Below is the original code:
var v = (v) ? v : {};
v.someClassA = (function() {
this.hello = function() {
console.log("Class A Hello!");
}
});
v.someClassB = (function() {
this.hello = function() {
console.log("Class B Hello!");
}
});
// this all works!
var myClassA = new v.someClassA();
var myClassB = new v.someClassB();
v.myCustomProperty = (function() {
function someClassC() {
this.hello = function() {
console.log('C');
}
}
function someClassD() {
this.hello = function() {
console.log('D');
}
}
return {
someClassC: someClassC,
someClassD: someClassD
}
});
// Uncaught TypeError: v.myCustomProperty.someClassC is not a function! Why?
var myClassC = new v.myCustomProperty.someClassC();
var myClassD = new v.myCustomProperty.someClassD();
myClassA.hello();
myClassB.hello();
myClassC.hello();
myClassD.hello();
If I change my declaration of v.myCustomProperty to use object literal notation, then it ALL WORKS! :
v.myCustomProperty = {
someClassC: function() {
this.hello = function() {
console.log('C');
}
},
someClassD: function() {
this.hello = function() {
console.log('D');
}
}
}
I guess my question really is how would I make this work using the notation in my original snippet? Possible? Horrible practice to do it that way?
Thanks!
v.myCustomProperty is a function that returns an object. You have to call the function first:
new (v.myCustomProperty().someClassC)();
// ^^
Otherwise, v.myCustomProperty.someClassC() tries to access the property someClassC of the function, and we all know (hopefully) that functions don't have such a property.
Or maybe you intended to execute the function immediately and assign the object to myCustomProperty?
v.myCustomProperty = (function() {
// ...
}()); // <- call function

JavaScript inheritance without the 'new' keyword

I'm used to using this pattern all over my code, and I like it:
var UserWidget = (function(){
var url = "/users",
tmpl = "#users li", $tmpl;
function load() {
$tmpl = $(tmpl);
$.getJSON(url, function(json){
$.each(json, function(i, v) {
appendUser(v);
});
});
}
...
return {
load: load
};
})();
However, I have many "widget" objects. "ProfileWidget", "PlayerWidget" etc etc. and there's certain actions that each widget share. So ideally, if we're thinking object-orientally, I want each widget object to inherit some methods from a main "Widget" class.
How can I do this without changing this lovely pattern I've been using?
To be more clear I'd like to be able to do something like this:
var Widget = (function() {
function init() {
console.log("wow yeah");
}
})();
// have UserWidget inherit somehow the Widget stuff
var UserWidget = (function() { ...
UserWidget.init(); // -> "wow yeah"
Keep in mind these solutions are not what I'd typically reccomend and they are just to satisfy the question.
What about closing over everything so that its accessible from your "sub classes" (demo)
var Widget = (function () {
var init = function () {
console.log("wow yeah");
};
var User = (function () {
var load = function () {
init();
};
return {
'load': load
};
} ());
return { 'User': User };
} ());
// Usage: This loads a user and calls init on the "base"
Widget.User.load();
Another way (demo) that you might like is to just use proper inheritance, but within the closure and then return one and only one instance of that new function. This way lets you keep User and whatever else an object
// Closing around widget is completely unneccesarry, but
// done here in case you want closures and in case you
// dont want another instance of widget
var Widget = (function () {
// definition that we'll end up assigning to Widget
function widget() {
console.log("base ctor");
}
// sample method
widget.prototype.init = function () {
console.log("wow yeah");
};
// put widget in Widget
return widget;
} ());
var User = (function () {
function user() { }
user.prototype = new Widget();
// TODO: put your User methods into user.prototype
return new user();
} ());
var Player = (function () {
function player() { }
player.prototype = new Widget();
// TODO: put your Player methods into player.prototype
return new player();
} ());
User.init();
Player.init();
I decided to use Crockford's object:
// function from Douglas Crockford, comments from me
function object(o) {
// define a new function
function F() {}
// set the prototype to be the object we want to inherit
F.prototype = o;
// return a new instance of that function, copying the prototype and allowing us to change it without worrying about modifying the initial object
return new F();
}
// Usage:
var Widget = (function() {
function init() {
console.log("wow yeah");
}
return {
init: init
};
})();
var UserWidget = (function() {
var self = object(Widget); // inherit Widget
function priv() {}
self.pub = "boom";
...
return self;
})();
UserWidget.init() // -> "wow yeah"
This works great for me and I like it!
You could use Object.create(obj), which I believe is what you're looking for.
Without using new, you'll have to use the __proto__ property rather than prototype, so this won't work in all browsers.
var Widget = {
init: function () {
console.log("wow yeah");
}
};
var UserWidget = (function(){
var url = "/users",
tmpl = "#users li",
$tmpl;
function load() {
$tmpl = $(tmpl);
$.getJSON(url, function(json){
$.each(json, function(i, v) {
appendUser(v);
});
});
}
return {
load: load
};
})();
UserWidget.__proto__ = Widget;
UserWidget.init();
Demo: http://jsfiddle.net/mattball/4Xfng/
Here's a simple example of prototyping in JS... For more detail on this topic read "JavaScript: The Good Parts"
// widget definition
var Widget = {
init: function () {
alert('wow yeah!');
}
};
// user widget definition
var UserWidget = function () { };
UserWidget.prototype = Widget;
UserWidget.prototype.load = function () { alert('your code goes here'); }
// user widget instance
var uw = new UserWidget();
uw.init(); // wow yeah!
uw.load(); // your code goes here
Hope this helps!

Is it possible to append functions to a JS class that have access to the class's private variables?

I have an existing class I need to convert so I can append functions like my_class.prototype.my_funcs.afucntion = function(){ alert(private_var);} after the main object definition. What's the best/easiest method for converting an existing class to use this method? Currently I have a JavaScript object constructed like this:
var my_class = function (){
var private_var = '';
var private_int = 0
var private_var2 = '';
[...]
var private_func1 = function(id) {
return document.getElementById(id);
};
var private_func2 = function(id) {
alert(id);
};
return{
public_func1: function(){
},
my_funcs: {
do_this: function{
},
do_that: function(){
}
}
}
}();
Unfortunately, currently, I need to dynamically add functions and methods to this object with PHP based on user selected settings, there could be no functions added or 50. This is making adding features very complicated because to add a my_class.my_funcs.afunction(); function, I have to add a PHP call inside the JS file so it can access the private variables, and it just makes everything so messy.
I want to be able to use the prototype method so I can clean out all of the PHP calls inside the main JS file.
Try declaring your "Class" like this:
var MyClass = function () {
// Private variables and functions
var privateVar = '',
privateNum = 0,
privateVar2 = '',
privateFn = function (arg) {
return arg + privateNum;
};
// Public variables and functions
this.publicVar = '';
this.publicNum = 0;
this.publicVar2 = '';
this.publicFn = function () {
return 'foo';
};
this.publicObject = {
'property': 'value',
'fn': function () {
return 'bar';
}
};
};
You can augment this object by adding properties to its prototype (but they won't be accessible unless you create an instance of this class)
MyClass.prototype.aFunction = function (arg1, arg2) {
return arg1 + arg2 + this.publicNum;
// Has access to public members of the current instance
};
Helpful?
Edit: Make sure you create an instance of MyClass or nothing will work properly.
// Correct
var instance = new MyClass();
instance.publicFn(); //-> 'foo'
// Incorrect
MyClass.publicFn(); //-> TypeError
Okay, so the way you're constructing a class is different than what I usually do, but I was able to get the below working:
var my_class = function() {
var fn = function() {
this.do_this = function() { alert("do this"); }
this.do_that = function() { alert("do that"); }
}
return {
public_func1: function() { alert("public func1"); },
fn: fn,
my_funcs: new fn()
}
}
var instance = new my_class();
instance.fn.prototype.do_something_else = function() {
alert("doing something else");
}
instance.my_funcs.do_something_else();
As to what's happening [Edited]:
I changed your my_funcs object to a private method 'fn'
I passed a reference to it to a similar name 'fn' in the return object instance so that you can prototype it.
I made my_funcs an instance of the private member fn so that it will be able to execute all of the fn methods
Hope it helps, - Kevin
Maybe I'm missing what it is you're trying to do, but can't you just assign the prototype to the instance once you create it? So, first create your prototype object:
proto = function(){
var proto_func = function() {
return 'new proto func';
};
return {proto_func: proto_func};
}();
Then use it:
instance = new my_class();
instance.prototype = proto;
alert(instance.prototype.proto_func());

javascript error while creating object

While i am trying to create object like this
new Ext.TitleCheckbox ()
I am getting "not a constructor error"
my Object is
Ext.TitleCheckbox = {
checked:false,
constructor : function() {
},
getHtml : function (config) {
var prop = (!config.checked)?'checkbox-checked':'checkbox-unchecked';
var html = config.title+'<div class="'+prop+'" onclick="Ext.TitleCheckbox.toggleCheck(this)"> </div>';
return html;
},
toggleCheck : function (ele){
if(ele.className == 'checkbox-checked') {
ele.className = 'checkbox-unchecked';
}
else if(ele.className == 'checkbox-unchecked') {
ele.className = 'checkbox-checked';
}
},
setValue : function(v){
this.value = v;
},
getValue : function(){
return this.value;
}
};
whats the mistake in here?
Ext.TitleCheckbox is not a function, you cannot make a function call to an object literal.
If you want to use the new operator, you should re-structure your code to make TitleCheckbox a constructor function.
Something like this (assumming that the Ext object exists):
Ext.TitleCheckbox = function () {
// Constructor logic
this.checked = false;
};
// Method implementations
Ext.TitleCheckbox.prototype.getHtml = function (config) {
//...
};
Ext.TitleCheckbox.prototype.toggleCheck = function (ele) {
//...
};
Ext.TitleCheckbox.prototype.setValue = function (v) {
//...
};
Ext.TitleCheckbox.prototype.getValue = function () {
//...
};
See CMS's answer for why. As a work-around, if you really need to do this, you can do it via inheritence. In javascript Constructors inherit from objects (a constructor is just a function). So:
function MyCheckbox () {} ; /* all we really need is a function,
* it doesn't actually need to do anything ;-)
*/
// now make the constructor above inherit from the object you desire:
MyCheckbox.prototype = Ext.TitleCheckbox;
// now create a new object:
var x = new MyCheckbox();

Categories

Resources