I've been stumped on this problem for a few hours now, and I can't seem to find a solution. I have a class that extends a parent class, but I cannot seem to access the variables declared in the parent's constructor.
I borrowed my inheritance technique. It simply uses an "extend" function to create the subclass:
//In functions.js
function extend(base, sub, methods) {
sub.prototype = Object.create(sub.prototype);
sub.prototype.constructor = sub;
sub.base = base.prototype;
for(var name in methods) { sub.prototype[name] = methods[name]; }
return sub;
}
I create a class called Stimulus that serves as a function:
//In classes.js
function Stimulus(module_id, unit_id, attributes) {
this.attributes = attributes;
this.module_id = module_id;
this.unit_id = unit_id;
//create some other class variables based on this.attributes, this.module_id, and this.unit_id
}
Stimulus.prototype = {
_getStimulus: function() { //retrieve from database }
//other functions here
}
And finally I have the subclass. The technique I use to create it is also borrowed from the above link:
//In classes.js
ImageStimulus = (function() {
var $this = function(module_id, unit_id, attributes) {
$this.base.constructor.call(this, module_id, unit_id, attributes);
};
extend(Stimulus, $this, {
initialize: function() {
this.fixation_cross = this.attributes['Fixation Cross'] ? this.attributes['Fixation Cross'] : false;
//do other stuff
}
//other functions here
});
return $this;
})();
It all seems straightforward enough. However, in my main script, when I try to run this, I create the object and then try to run the initialize() function and everything falls apart:
//In main.js
var stimulus_objects = [];
for(var i = 0; i < someLimit; i++) {
//module_id is passed directly to this function
var unit_id = //some source;
var stimulus_attributes = //some source;
stimulus_objects[i] = new ImageStimulus(module_id, unit_id, stimulus_attributes);
stimulus_objects[i].initialize();
}
If I check the console I see that is says
Uncaught TypeError: Cannot read property 'Fixation Cross' of undefined
And it corresponds to the line in ImageStimulus.initialize() where I try to call on this.attributes['Fixation Cross'].
It seems that something is going wrong in making Stimulus the prototype of ImageStimulus, because ImageStimulus.initialize() cannot access the this.attributes variables that is created in the constructor for the Stimulus class.
Does anybody else see the error?
I have a decent amount of OOP programming in Java, C++, and even PHP, but this is my first attempt at JavaScript OOP, and so I feel like I'm probably making some simple mistake.
EDIT: Solved the problem... somehow.
So it seems there was a fairly trivial solution. The Stimulus function was never being called, and it should have been called on the line with $this.base.constructor.call(). In the Stimulus.prototype object I added constructor: Stimulus and now Stimulus is being called properly. It seems odd that I had to do that (shouldn't Stimulus() be it's own constructor?), but it works!
Stimulus.prototype = {
constructor: Stimulus,
_getStimulus: function() {...
Does anyone know why that occurred and why my fix worked? I'm trying to understand what I did.
I get it. You're replacing the entire prototype, so it is killing the constructor. By setting the constructor explicitly, you are putting it back. The alternative is to set the prototype method directly rather than setting the entire prototype.
For this fiddle to be useful, bring up the console and set a break point before clicking Run.
http://jsfiddle.net/x2v7wv6j/
//In functions.js
function extend(base, sub, methods) {
sub.prototype = Object.create(sub.prototype);
sub.prototype.constructor = sub;
sub.base = base.prototype;
for(var name in methods) { sub.prototype[name] = methods[name]; }
return sub;
}
//In classes.js
function Stimulus(module_id, unit_id, attributes) {
this.attributes = attributes;
this.module_id = module_id;
this.unit_id = unit_id;
//create some other class variables based on this.attributes, this.module_id, and this.unit_id
}
Stimulus.prototype._getStimulus = function() { //retrieve from database
}
//other functions here
//In classes.js
ImageStimulus = (function() {
var $this = function(module_id, unit_id, attributes) {
$this.base.constructor.call(this, module_id, unit_id, attributes);
};
extend(Stimulus, $this, {
initialize: function() {
this.fixation_cross = this.attributes['Fixation Cross'] ? this.attributes['Fixation Cross'] : false;
//do other stuff
}
//other functions here
});
return $this;
})();
var foo = function (module_id) {
var stimulus_objects = [];
var someLimit = 10;
for(var i = 0; i < someLimit; i++) {
//module_id is passed directly to this function
var unit_id = "some source";//some source;
var stimulus_attributes = "some source"; //some source;
stimulus_objects[i] = new ImageStimulus(module_id, unit_id, stimulus_attributes);
stimulus_objects[i].initialize();
}
}
foo(1);
Related
This one is something that is fairly easy to do in PHP and I find my self in a situation where it would come in handy, but I do not believe the PHP trick will work.
Basically I want to use a variable passed from a function within an object to then reinitialize that object using the child (defined by the variable).
var View = function(){
var fn = this;
fn.load = function(name){
return new name();
}
}
var view = View.load('titleView');
This is a very early work on it, so forgive the fact that it looks so strange (still need to tinker more with the concept). But overall it should roughly show the concept.
Is there a way to basically recreate the current functions instance with a new function? To do this in the aspect I am thinking of I will need to use a variable rather then pass the new object. Is this possible? I am sure in some form it has to be. Any ideas/pointers? Google has been failing me since I am not sure of the right keywords for this.
EDIT:
should also show the idea behind the "titleView" class
var titleView = function(){}
titleView.prototype = new View;
I think the easiest way to do this is via some kind of factory that can produce the types of views you are wanting. Something like this:
var View = (function() {
var registry = {};
return {
register: function(type, fn) {
if (typeof registry[type] === 'undefined') {
registry[type] = fn;
return true;
}
return false;
},
load: function(type) {
return new registry[type]();
}
};
})();
var titleView = function() {
this.name = 'titleView';
};
var subTitleView = function() {
this.name = 'subTitleView';
}
View.register('titleView', titleView);
View.register('subTitleView', subTitleView);
var view = View.load('titleView');
console.log("Created view type: " + view.name);
view = View.load('subTitleView');
console.log("Created view type: " + view.name);
This would give the following (allowing you to recreate view variable on the fly):
// Created view type: titleView
// Created view type: subTitleView
If you try to do it the way your example is going, you'll have to use subclasses like so:
function Base() {
this.name = "Base";
this.load = function(fn) {
fn.apply(this);
}
}
function Other() {
Base.apply(this, arguments);
this.name = "Other";
}
var view = new Base();
console.log(view);
view.load(Other);
console.log(view);
// => Base { name: "Base", load: function }
// => Base { name: "Other", load: function }
However, with this method, after calling view.load(Other), your view will still retain whatever properties/methods it had prior to calling load (which may not be what you want).
think this is what ur asking
var View = function(){
this.load = function(name){
return name;
}
}
var myView = new View;
var v = myView.load('titleView');
alert(v);
I am trying to make a parent data access layer class that is inherited by multiple classes.
parent class:
var DataAccess = function() {
this.Save = function(){
alert(this.ListName); //works
SaveLogic(this.Id); //doesnt work
}
}
Child Class:
var Job = function(){
Job.prototype.ListName = 'MyList'; //works
this.Save = function(){
Job.prototype.Save().call(this);
//specific Job Save logic
}
}
Job.prototype = new DataAccess();
Now in my main class:
var aJob = new Job();
aJob.Id = 1;
aJob.Save(); //Does not work. Prototype can not see aJob.Id..
As you can see, I need to create a parent function with shared variables such as ID, so when I inherit the parent class, I can assign values to these variables so the shared logic of hte parents class work, then my extended class's can have specific logic
You can start with construction like this:
var DataAccess = function() {
this.Save = function(){
console.log('DataAccess Save call', this.ListName, this.Id);
}
}
var Job = function(){
this.ListName = 'MyList';
}
Job.prototype = new DataAccess();
/**
* Delete me to use parent's Save method.
*/
Job.prototype.Save = function(){
console.log('Job Save call', this.ListName, this.Id);
}
var aJob = new Job();
aJob.Id = 1;
aJob.Save();
#stivlo described how it works in his answer here: https://stackoverflow.com/a/4778408/1127848
The problem I had was I wanted to reuse the same code. I think I have worked it out this way, im still not 100% its the right way to go with prototype programming :
function DataAccess() {
//setup common variables
}
DataAccess._Save_(listname, id){
commonSaveLogic(id);
doStuff(listname);
}
function Job() {
this.ListName = 'Jobs';
DataAccess.call(this); //call DataAccess Constructor
}
Job.prototype = DataAccess;
Job.prototype.constructor = Job;
Job.ProtoType.Save = function(){
this._Save_(this.ListName, this.Id);
}
function AotherList() {
this.ListName = 'AnotherList';
DataAccess.call(this);
}
//same as above. Job and Another list both inherit off DataAccess.
Dont use .prototype inside the constructor. We define .prototype for sharing same copy to all objects.
You are missing here many things. I'm explaining one by one:
First : SaveLogic(this.Id); //doesnt work
Because You don't use this with the function so it's a global function not a constructor function. And you don't have defined it any where so there will be an error like function SaveLogic not defined
To prevent this error, define the function somewhere.
Second : You have passed this.Id as a parameter. Id using the line aJob.Id = 1; will not be accessible within the SaveLogic(this.Id); because Id is a property of aJob not of ajob.prototype. this.ListName will be available here because it's a property of prototype.
So it you want to get Id inside SaveLogic() function, define it as prototype property.
Third : when this line aJob.Save(); will be invoke it will call
this.Save = function(){
Job.prototype.Save().call(this);
//specific Job Save logic
}
Job.prototype.Save() will search for a function named as Save(). Which is not defined in Job's prototype so function not defined error will occur.
Fourth : call() can not be called anyhow excepts either DataAccess.call() or Job.call();
call() is just like the constructor call excepts it's first parameter get assigned to the constructor's this object.
Here i have improved your code. Just copy and paste it in your editor and see what is going here.
Try this :
function SaveLogic(Id)
{
alert(Id);
}
var DataAccess = function() {
this.Save = function(){
alert(this.ListName); //works
SaveLogic(this.Id);
return this; //doesnt work
}
this.call = function() {
alert('call is called here');
}
}
var Job = function(){
Job.prototype.ListName = 'MyList'; //works
this.Save = function(){
//console.log(Job.prototype.Save());
Job.prototype.Save().call(this);
//specific Job Save logic
}
}
Job.prototype = new DataAccess();
var aJob = new Job();
Job.prototype.Id = 1;
aJob.Save(); //Does not work. Prototype can not see aJob.Id..
Considering object creation patterns with private properties, one way to do is :
function MyStack (){
var list = [],
index = 0;
this.push = function(val){
return list[index++] = val;
};
this.pop = function(){// ...}
}
var stack1 = new MyStack(); stack1.push(5);
var stack2 = new MyStack(); stack2.push(11);
Problem with this: Every instance of Stack has it's own copy of methods 'push' and 'pop'.
Another way for implementing constructor method is:
function MyStack(){
this.list = [];
this.index = 0;
}
MyStack.prototype = {
insert: function(val){
return this.list[this.index++] = val;
},
pop:function(){//...}
}
Problem here: We lose the privacy of list and index.
Is there a way, such that we can have both methods reuse among instances and privacy of properties ?
I understand that we can have this for methods that don't operate on any state of the object, but I am talking more about those methods that do operate on the state.
Yes. I've edited this code so it's actually fully functional as you had intended it to work. It seems a bit redundant to me, but, it does provide you the ability to provide a public interface, but to keep your variables private and control the way the user interacts with them.
function MyStack(){
var list = [];
var index = 0;
this.getIndex = function(){
return index;
}
this.setIndex = function(val){
index = val;
}
this.list = function(val){
if(val){
// setter if a value was provided. Illustrating how you can control
// index, which I assume is the point of having these things private
// to begin with
return list[this.setIndex(this.getIndex() + 1)] = val;
}
// always return list - acts like a getter
return list;
}
}
MyStack.prototype = {
insert: function(val){
return this.list(val);
},
pop:function(){}
}
var stack1 = new MyStack();
stack1.insert(5);
var stack2 = new MyStack();
stack2.insert(11);
You should check out John Resig's Simple Javascript Inheritance. It is a great read, and it has been extended to provide support for privates, aptly called Privates.js;
A constructor function may return any object (not necesserily this). One could create a constructor function, that returns a proxy object, that contains proxy methods to the "real" methods of the "real" instance object. This may sound complicated, but it is not; here is a code snippet:
var MyClass = function() {
var instanceObj = this;
var proxyObj = {
myPublicMethod: function() {
return instanceObj.myPublicMethod.apply(instanceObj, arguments);
}
}
return proxyObj;
};
MyClass.prototype = {
_myPrivateMethod: function() {
...
},
myPublicMethod: function() {
...
}
};
The nice thing is that the proxy creation can be automated, if we define a convention for naming the protected methods. I created a little library that does exactly this: http://idya.github.com/oolib/
I think in both approaches you mentioned, When ever object is created using constructor pattern the properties will get copied to its objects. This you mentioned for the 1st approach as the concern. I feel the same will be applied for the second approach also along with your concern in this approach.
We generally go to the second approach you mentioned when ever we want to extend the properties of "MyStack" to some other class.
Lets say i want to extend your class MyStack to MyTest like below
var dummy = function();
dummy.prototype = MyStack.prototype;
var MyTest = function(){
};
MyTest.prototype = new dummy(); // Assigning MyStack properties to MyTest
var obj = new MyTest();
I'm having a little trouble working out how my JavaScript should be structured, etc..
My OOP skills in languages such as PHP, ActionScript 3 and so on are what I'm assuming to be on-par, but JS is lacking this which has thrown me off quite a bit in my learning.
I have a vague understanding of the prototype feature which I used a little in AS2 - I believe this is the closest I'll be able to get. At the moment, I'm laying out my code similar to this:
var slideshow =
{
property: value,
/**
* This is a method
*/
myMethod: function()
{
// do method things
}
};
// ------
slideshow.property ++;
slideshow.myMethod();
This all works okay, but it's void my ability to do something like:
var myslideshow1 = new Slideshow();
var myslideshow2 = new Slideshow();
myslideshow1.property = 10;
myslideshow2.property = 16;
I'm not sure on how to go about creating two different instances of one "object" I've created (in this case, slideshow).
I can't find any resources that explain the prototype feature in a way that makes sense.
Any pointers would be supoib.
Any javascript function can act as a constructor for a class, so try this:
function SlideShow(params) {
return {
property: value,
myMethod: function() {
//do method things
};
};
};
var slideshow1 = new SlideShow(params);
slideshow1.property = 10;
//etc.
I would frown apon using prototype to add methods to a class as there could be performance issues
Here is a sample class structure you could use. JavaScript classes are not much different the functions.
function MyItem(){
this.d = '';
this.clear = function( ) {
this.d = '';
}
}
var myItem = new MyItem( )
myItem.d = "test";
alert(myItem.d);
myItem.clear();
alert(myItem.d)
Some good reading here
You should avoid using the new operator, everything is public. A better way to do what you want to do, and have private variables and functions is to do the following:
var slideshow = function () {
var self = {};
var private_param = "hello";
var private_func = function(say) {
alert(say);
};
var method = function() {
private_func(private_param);
};
var param = 500;
self.method = method;
self.param = param;
return self;
// return object, with the method func and param param publicly accessible
// private_param and private_func are not accessible to the outside
};
var presentation = slideshow(); // new slideshow, you could edit to pass in init params
presentation.method(); // hello
I need to extend a class, which is encapsulated in a closure. This base class is following:
var PageController = (function(){
// private static variable
var _current_view;
return function(request, new_view) {
...
// priveleged public function, which has access to the _current_view
this.execute = function() {
alert("PageController::execute");
}
}
})();
Inheritance is realised using the following function:
function extend(subClass, superClass){
var F = function(){
};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
subClass.superclass = superClass.prototype;
StartController.cache = '';
if (superClass.prototype.constructor == Object.prototype.constructor) {
superClass.prototype.constructor = superClass;
}
}
I subclass the PageController:
var StartController = function(request){
// calling the constructor of the super class
StartController.superclass.constructor.call(this, request, 'start-view');
}
// extending the objects
extend(StartController, PageController);
// overriding the PageController::execute
StartController.prototype.execute = function() {
alert('StartController::execute');
}
Inheritance is working. I can call every PageController's method from StartController's instance. However, method overriding doesn't work:
var startCont = new StartController();
startCont.execute();
alerts "PageController::execute".
How should I override this method?
It doesn't work because StartController calls PageController which adds an execute property to your object, so the execute property of StartController.prototype is not used.
For your overriding to work, you have to either :
1) define PageController.prototype.execute as the execute method of PageController. It won't work because then the function doesn't have access to _current_view.
2) define StartController.execute in the object constructor :
var StartController = function(request){
// calling the constructor of the super class
StartController.superclass.constructor.call(this, request, 'start-view');
// overriding the PageController::execute
this.execute = function() {
alert('StartController::execute');
}
}
// extending the objects
extend(StartController, PageController);
edit:
So you want for StartController.execute to access _current_view, which is impossible as long as _current_view is part of a closure that StartController is not part of. You might have to proceed like this:
(function () {
var _current_view;
window.PageController = function(request, new_view) {
...
this.execute = function() { ... }
}
window.StartController = function(request) {
StartController.superclass.constructor.call(this, request, 'start-view');
this.execute = function() { ... }
}
extend(StartController, PageController);
}()
var startCont = new StartController();
startCont.execute();
And if you want some kind of protected behavior, you might want to try this trick:
(function() {
var token = {};
window.Class1 = function() {
this.protectedMethod = function(tok) {
if(tok != token) return; // unauthorized
...
}
}
window.Class2 = function() {
new Class1().protectedMethod(token); // access granted
}
})()
new Class1().protectedMethod(); // access denied
There's no such thing as a package in javascript, so your possibilities are limited. You can certainly not have any kind of privileges among functions/objects/constructors that are not part of the same script. None that I know of, at least. Except maybe querying a server for some kind of authorization.