testA = function(){
this._data = [];
}
testA.prototype.add = function(data){
this._data.push(data) // Here i am facing this._data is undefined
}
testB = function(){}
testB.prototype = Object.create(testA);
var instance = new testB;
testB.prototype.pushData = function(data){
instance.prototype.add(data);
}
function (data){
instance.prototype.add(data);
}
instance.pushData("someData")
In the above javascript snippet, i am facing 'Cannot read property 'push' of undefined' error
Is my inheritance concepts is wrong? can any one help to resolve this inheritance issue
You have several mistakes/inaccuracies in your code:
It is cleaner to write like this to preserve prototypal inheritance
testB.prototype = Object.create(testA.prototype);
to make the instance object have _data property you have to call
first the testA (parent) in the testB.
testB = function() { testA.call(this); }
I don't know what you meant but this function looks weird:
function (data){ instance.prototype.add(data); }
Inside testB you can use this to make reference to parent's (testA's) properties in order to call add() function: this.add(data); This function will be first searched in testB's properties and then through testB's __proto__ reference in testA prototype object (see point #1 above).
In the end it all might look like this:
testA = function() {
this._data = [];
}
testA.prototype.add = function(data){
this._data.push(data);
//console.log(this._data);
}
testB = function() {
testA.call(this);
}
testB.prototype = Object.create(testA.prototype);
testB.prototype.pushData = function(data){
this.add(data);
}
var instance = new testB();
instance.pushData("someData");
console.log(instance._data);
UPDATE
testA = function() {
this._data = [];
}
testA.prototype.add = function(data){
var data = data + ' from parent testA';
this._data.push(data);
}
testB = function() {
testA.call(this);
}
testB.prototype = Object.create(testA.prototype);
testB.prototype.add = function(data){
testA.prototype.add.call(this, data); // call to parent's add(); data is passed to the parent's add() function too.
// now you can do some actions intended for testB like:
var data = data + ' from child testB';
this._data.push(data);
}
var instance = new testB();
instance.add("someData");
console.log(instance._data);
Also you can do like this:
var a = new testA();
a.add(); // add() in testA prototyp is called
var b = new testB();
b.add(); // add() in testB prototyp is called
Without creating an instance of testA you can call its add() method from any place only like this:
testA.prototype.add.call(context, data);
to summarize, there are 3 options (in fact 2): create instance of testA and call its add(), call testA's add() from testB's add() and call testA's add() wherever you want using call()/apply().
There are some really smart OOP patterns out there that allow you to have references in one object to another one and do some fancy calls. But it is a vast topic (OOP) and is beyond the scope of this question I suppose.
Related
Im doing a course in frontend Dev in uni and the teacher insists on us using a old book so to learn the history and basics of JavaScript before we move on to more advanced and recent implementations.
Now in this book we are instructed to code a webpage for a food truck and it is supposed to take orders.
Now in some scripts the objects are defined like this :
function DataStore() {
this.data = {};
}
Here the data object is defined using the keyword "this" as in saying it belongs to the function object DataStore.
however in some scripts the data object is defined as:
FormHandler.prototype.addSubmitHandler = function() {
console.log('Setting submit handler for form');
this.$formElement.on('submit', function(event){
event.preventDefault();
var data = {};
My question is what is the difference in the two data objects?
Short answer is at the bottom
Long and boring introduction to how things work
When you write this :
function SomeThing() { }
You can always do
let a = new SomeThing();
even when it doesn't make sense like in :
function lel() { console.log('lelelel'); }
let g = new lel();
console.log(g);
console.log(g.constructor.name);
What this means is that classes are actually the same as functions. And a function in which you use the keyword this usually means you will want to create instances of it.
now if I want all instances of my lel() function class to have a property called foo and a method called bar here's how you do :
lel.prototype.foo = "Some initial value";
lel.prototype.bar = function() {
console.log(this.foo);
}
now I can do
let g = new lel();
lel.bar();
lel.foo = "Hell yeah !";
lel.bar();
In conclusion, this :
function SomeThing() {
this.data = {};
}
SomeThing.prototype.setData = function(key, value) {
this.data[key] = value;
}
SomeThing.prototype.getDataKeys = function() {
return Object.keys(this.data);
}
SomeThing.prototype.getDataValues = function() {
return Object.values(this.data);
}
is the same thing as this
class SomeThing {
constructor() {
this.data = {};
}
setData(key, value) {
this.data[key] = value;
}
getDataKeys() {
return Object.keys(this.data);
}
getDataValues() {
return Object.values(this.data);
}
}
Clarifications about your question
If somewhere in your code you have :
FormHandler.prototype.addSubmitHandler = function() {
console.log('Setting submit handler for form');
this.$formElement.on('submit', function(event){
event.preventDefault();
var data = {};
if necessarily means that somewhere else in your code you have
function FormHandler(...) { ... }
Short answer
This :
function DataStore() {
this.data = {};
}
is how you define a class named DataStore with a property called data initialized to the value {}
And this :
FormHandler.prototype.addSubmitHandler = function() {
...
var data = {};
}
is how you add a method called addSubmitHandler to the already defined class FormHandler. That method uses a local variable called data, could have been any other name
In the first case, data is a property of the object that is created like this: new DataStore.
You can access this property like this:
var obj = new DataStore();
obj.data // => {}
/* or */
obj['data'] // => {}
In the second case, data is just a global variable, inside of an event handler, that is added executing the function.
var obj = new FormHandler();
obj.addSubmitHandler();
You access this variable like this:
data // => {}
I don't think it's a good idea to learn old JS. You would be out of date. You wouldn't be able to use latest technologies, and it would be harder to get a job.
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..
I have a method in a base class that I want to keep in a subclass, but just add to it. I've found lots of stuff on augmenting classes and objects with properties and methods, but I can't find, or don't understand, how to just augment the method. The worst case scenario is that I would have to paste the entire method of the parent class into the subclass, but that seems like duplicate code... please help
function someObject (){
this.someProperty = 1;
this.incrementProperty = function incrementProperty(){
this.propertyOfSomeObject += 1;
}
}
function newObject (){
someObject.call(this);
this.incrementProperty = function incrementProperty(){
//do everything the super class has for this property already
return this.someProperty;
}
}
var incrementer = new newObject;
alert (incrementer.incrementProperty()); //I want output to be 2
// parent object
function someObject () {
this.someProperty = 1;
}
// add incrementProperty to the prototype so you're not creating a new function
// every time you instantiate the object
someObject.prototype.incrementProperty = function() {
this.someProperty += 1;
return this.someProperty;
}
// child object
function newObject () {
// we could do useful work here
}
// setup new object as a child class of someObject
newObject.prototype = new someObject();
// this allows us to use "parent" to call someObject's functions
newObject.prototype.parent = someObject.prototype;
// make sure the constructor points to the right place (not someObject)
newObject.constructor = newObject;
newObject.prototype.incrementProperty = function() {
// do everything the super class has for this property already
this.parent.incrementProperty.call(this);
return this.someProperty;
}
var incrementer = new newObject();
alert (incrementer.incrementProperty()); // I want output to be 2
See: http://jsfiddle.net/J7RhA/
this should do, you have to use prototype to have a real concept of oo with javascript
function someObject (){
this.someProperty = 1;
this.propertyOfSomeObject = 0;
this.incrementProperty = function incrementProperty(){
this.propertyOfSomeObject += 1;
return this.propertyOfSomeObject;
}
}
function newObject (){
someObject.call(this);
this.incrementProperty = function incrementProperty(){
this.__super__.incrementProperty.apply(this);
return this.propertyOfSomeObject + 1;
}
}
newObject.prototype = new someObject()
newObject.prototype.__super__ = newObject.prototype
var incrementer = new newObject();
alert(incrementer.incrementProperty()); //I want output to be 2
experiment removing incrementProperty from newObject and it will return 1
I usually use the augment library to write classes in JavaScript. This is how I would rewrite your code using augment:
var Foo = Object.augment(function () {
this.constructor = function () {
this.someProperty = 1;
};
this.incrementProperty = function () {
this.someProperty++;
};
});
var Bar = Foo.augment(function (base) {
this.constructor = function () {
base.constructor.call(this);
};
this.incrementProperty = function () {
base.incrementProperty.call(this);
return this.someProperty;
};
});
As you can see since Bar extends Foo it gets Foo.prototype as a parameter (which we call base). This allows you to easily call the base class constructor and incrementProperty functions. It also shows that the constructor itself is just another method defined on the prototype.
var bar = new Bar;
alert(bar.incrementProperty());
The output will be 2 as expected. See the demo for yourself: http://jsfiddle.net/47gmQ/
From this answer:
Overriding functions
Sometimes children need to extend parent functions.
You want the 'child' (=RussionMini) to do something extra. When RussionMini can call the Hamster code to do something and then do something extra you don't need to copy and paste Hamster code to RussionMini.
In the following example we assume that a Hamster can run 3km an hour but a Russion mini can only run half as fast. We can hard code 3/2 in RussionMini but if this value were to change we have multiple places in code where it needs changing. Here is how we use Hamster.prototype to get the parent (Hamster) speed.
// from goog.inherits in closure library
var inherits = function(childCtor, parentCtor) {
function tempCtor() {};
tempCtor.prototype = parentCtor.prototype;
childCtor.prototype = new tempCtor();
childCtor.prototype.constructor = childCtor;
};
var Hamster = function(name){
if(name===undefined){
throw new Error("Name cannot be undefined");
}
this.name=name;
}
Hamster.prototype.getSpeed=function(){
return 3;
}
Hamster.prototype.run=function(){
//Russionmini does not need to implement this function as
//it will do exactly the same as it does for Hamster
//But Russionmini does need to implement getSpeed as it
//won't return the same as Hamster (see later in the code)
return "I am running at " +
this.getSpeed() + "km an hour.";
}
var RussionMini=function(name){
Hamster.apply(this,arguments);
}
//call this before setting RussionMini prototypes
inherits(RussionMini,Hamster);
RussionMini.prototype.getSpeed=function(){
return Hamster.prototype
.getSpeed.call(this)/2;
}
var betty=new RussionMini("Betty");
console.log(betty.run());//=I am running at 1.5km an hour.
If I use constructor functions for my objects and prototype for shared functionality I would like to mixin shared functionality (functions) to the object's prototype but instance specific (this varaibles) to the object instances.
To add the prototype part I found this pattern. To set instance variables that are assumed to be there by the prototype functions I came up with an init (one for each mixin).
Here is a simple example:
var mixIn=function(target,source){
for(fn in source){
if(source.hasOwnProperty(fn)){
target.prototype[fn]=source[fn];
}
}
};
var SpeakEnable = {
say:function(){
console.log(this.message);
},
initSpeak:function(){// for initializing instance vars
this.message="Hello World Mixed in!";
this.object=[];
}
};
var Person=function(){
this.initSpeak();//have to init instance vars
};
// set up inheritance
// set up Person.prototype
// set speak enable
mixIn(Person,SpeakEnable);
var lulu=new Person();
lulu.say();
var june=new Person();
console.log(june.say===lulu.say);//true
console.log(june.object===lulu.object);//false
This all works fine and dandy but initializing the instance variables is where I have some problem with. It somehow doesn't seem to be a very clean way. When I mix in several mixins the Person constructor function has to call all the init functions to set up the instance variables. Forgetting to call it will result in strange errors (in this case console logging undefined when say is called on an instance).
So the question is: is there a cleaner way to setup initial instance variables that are assumed to be there by the mixin functions?
You could inherit all mixable objects from a base object that ensures proper initialization. This is a clean way of achieving your goal.
The following code demonstrates this principle:
//------------ framework
var inherits = function(childCtor, parentCtor) {
function tempCtor() {};
tempCtor.prototype = parentCtor.prototype;
childCtor.superClass_ = parentCtor.prototype;
childCtor.prototype = new tempCtor();
childCtor.prototype.constructor = childCtor;
};
var mixIn=function(target,source){
for(fn in source){
if(source.hasOwnProperty(fn) && fn.name != 'init'){
target.prototype[fn]=source[fn];
}
}
if (typeof source.init == 'function') {
if (target.prototype._mixInits === undefined) {
target.prototype._mixInits = [];
}
target.prototype._mixInits.push(source.init);
}
};
// all objects that can be mixin's should inherit from
// this object in order to ensure proper initialization
var Mixable = function() {
var mixInits = this.__proto__._mixInits;
if (mixInits !== undefined) {
for (var i = 0; i < mixInits.length; i++) {
mixInits[i].call(this);
}
}
};
//------------ testcode
var SpeakEnable = {
say:function(){
console.log(this.message);
},
init:function(){
console.log('say init called');
this.message="Saying Hello World Mixed in!";
this.object=[];
}
};
var WalkEnable = {
walk:function(){
console.log(this.walk_message);
},
init:function(){
console.log('walk init called');
this.walk_message="Walking step 1.2.3.";
}
};
var Person=function() {
Mixable.call(this);
};
inherits(Person, Mixable);
mixIn(Person,SpeakEnable);
mixIn(Person,WalkEnable);
var lulu=new Person();
lulu.say();
lulu.walk();
How can I add data/functions to all instances of a javascript object created by a constructor so that all instances have the same reference and not a copy of it?
Basically implementing the equivalent of a static method in C#.
For example, given the following code which creates a Widget class.
(function() {
var Widget = function() {
};
Widget.prototype.init = function(data) {
this.data = data;
};
this.Widget = Widget;
}).call(this);
var instance1 = new Widget();
instance1.init('inst1');
var instance2 = new Widget();
instance2.init('inst2');
alert(instance1.data); // inst1
alert(instance2.data); // inst2
In the above case each instance has it's own copy of the data property. However I want to add a function that sets data for all current and future instances.
My current solution is to add a function to the constructor function object, not to it's prototype. See below for example. Is there any pitfalls to this and is there a better way?
(function() {
var Widget = function() {
};
Widget.prototype.init = function(data) {
this.data = data;
};
Widget.addStaticData = function(data) {
this.staticData = data;
};
Widget.prototype.getStaticData = function() {
return Widget.staticData;
};
this.Widget = Widget;
}).call(this);
var instance1 = new Widget();
instance1.init('inst1');
Widget.addStaticData('static');
var instance2 = new Widget();
instance2.init('inst2');
alert(instance1.data); // inst1
alert(instance2.data); // inst2
alert(instance1.getStaticData()); // static
alert(instance2.getStaticData()); // static
Three pitfalls that I can think of:
methodological: the prototype is the place for shared, reused, inherited functionality/properties - utilise it as such
performance: it is quicker to inherit than to set each time on an instance. John Resig (jQuery creator) did some benchmarking on this in a blog post that I appear unable to find at present.
losing the split between inherited and own properties. If you apply everything to an instance via the constructor, everything is an instance property.
Everything via constructor:
function Dog() { this.legs = 4; }
var fido = new Dog();
fido.name = 'Fido';
for (var i in fido) if (fido.hasOwnProperty(i)) alert(i+' = '+fido[i]);
...alerts both properties as they are deemed the instance's own.
Via prototype and constructor
function Dog2() { }
Dog2.prototype.legs = 4;
var fido = new Dog2();
fido.name = 'Fido';
for (var i in fido) if (fido.hasOwnProperty(i)) alert(i+' = '+fido[i]);
...alerts just name because that is the only instance property. (Nonetheless, fido.legs is retrievable - but it comes from the prototype).
[EDIT - in response to the OP's commet below]
If you want a static method, then that should be added to the function after its declaration.
function Dog() {}
Dog.static = function() {}
Consider a local variable staticData instead of the Widget.staticData property. That way, an external command won't be able to write the data directly, so the only way to write it will be through the addStaticData function:
(function () {
var Widget = function () {};
var staticData;
Widget.addStaticData = function ( obj ) {
staticData = obj.data;
};
Widget.prototype.init = function () {
var data = staticData;
// use data
// or just use the staticData variable directly
};
this.Widget = Widget;
}).call( this );
With your code, one could just execute this:
Widget.staticData = { data: 'COMPROMISED!' };
to change the static data. Since you have a dedicated function for setting the static data, you probably don't want it to be possible to change the static data in other ways.
With my code, the above statement has no effect, and the static data can only be changed via the addStaticData function.