I apologize because this topic comes up a lot, but I have not been able to have this adequately explained in anything I've read today.
I am trying to make a simple collection class (and learn about javascript prototyping at the same time) designed to store objects with a "name" property and lets its members be accessed by index or value. So far I've got this:
function Collection() {}
Collection.prototype.constructor = Collection;
Collection.prototype._innerList = [];
Collection.prototype._xref = {};
Collection.prototype.count = function () { return this._innerList.length; };
Collection.prototype.add = function (obj) {
this._xref[obj.name] = this._innerList.push(obj) - 1;
}
Collection.prototype.get = function (id) {
if (typeof id == "string") {
return this._innerList[this._xref[id]];
} else {
return this._innerList[id];
}
};
http://jsfiddle.net/YrVFZ/
The problem:
var foo = new Collection();
foo.add({name: "someitem", value:"hello world"}); // foo.count()== 1
var bar= new Collection();
bar.add({name: "someotheritem", value:"hello world"}); // bar.count()== 2
Hmm...
Basically, the new instance bar is created with all the properties having the current values of the data in foo. I know I can fix this by putting _xref, etc. inside the constructor, but I'm trying to understand how prototyping works. If I create a new instance, and make changes to the data in that instance, why would those values carry over when I create another new instance?
If I make further changes to a property from the prototype of foo or bar they are independent, so it doesn't seem as if I'm somehow referencing the same instance of anything. So what is causing bar to be instantiated with the current values from foo?
Consider a classroom full of students. Putting something on the prototype is like putting something on the white board for them all to see. When you're declaring
Collection.prototype._innerList = [];
you're giving every collection that property; regardless of calling new Collection() any changes to the white board affects all students. However, if you define it within the constructor, or one of the functions as this.variableName = [], each copy will have its own variableName, like handing each student a handout. Obviously, there's some cases when it's okay to have something on the white board, such as instructions that will be universal from student to student, but if each item is going to be different for each student, it should be an individual property. Hope this explanation makes sense...
You want to be doing this.
function Collection() {
if (!this instanceof Collection)
return new Collection();
this._innerList = [];
this._xref = {};
return this;
}
Collection.prototype.count = function() {
return this._innerList.length;
};
Collection.prototype.add = function(obj) {
this._xref[obj.name] = this._innerList.push(obj) - 1;
}
Collection.prototype.get = function(id) {
if (typeof id == "string") {
return this._innerList[this._xref[id]];
} else {
return this._innerList[id];
}
};
var foo = new Collection();
foo.add({name: "someitem", value:"hello world"});
console.log(foo.count()); // 1
var bar= new Collection();
bar.add({name: "someotheritem", value:"hello world"});
console.log(bar.count()); // 1
http://jsfiddle.net/vXbLL/
Edit
Not really relevant to your question, but it's something I do so I will throw it out there. Whenever I'm doing something on the prototype, if I'm not returning something, I return this. It allows chaining, so you could do instance.function1().function2().function3() as long as function1 and function2 return this.
You can think of a prototype as giving all objects of that class shared variables. Like static variables in a c++ class if that makes any sense. That's ok to do for functions because they're all the same for each instance of the class. However, if you want the object to have its own non-shared variable, you shouldn't use the prototype. One simple way to do it is to assign them in the constructor method like this:
function Collection()
{
this._innerList = [];
this._xref = {};
}
Collection.prototype.count = function () { return this._innerList.length; };
Collection.prototype.add = function (obj) {
this._xref[obj.name] = this._innerList.push(obj) - 1;
}
Collection.prototype.get = function (id) {
if (typeof id == "string") {
return this._innerList[this._xref[id]];
} else {
return this._innerList[id];
}
};
var foo = new Collection();
foo.add({name: "someitem", value:"hello world"}); // foo.count()== 1
document.write(foo.count(),"<br>");
var bar= new Collection();
bar.add({name: "someotheritem", value:"hello world"}); // bar.cou
document.write(bar.count(),"<br>");
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've been reading about JavaScript prototypal inheritance and the prototype property, and I started making a fiddle to understand the concepts better. But I need help understanding why my example isn't working as I thought it would.
In the following example, I'm trying to create a hierarchy where when you create an object, it tells you the name of the parent object. However, my logs always return "object".
One article explained that prototype chaining works so that if a property is not found on the object, the prototype of the parent is checked, and it keeps going up until 'Object', and if not found returns undefined.
Here's a fiddle to go along: http://jsfiddle.net/hqozqd0m/
Object.prototype.cname = 'object';
function plant() {
function parentname() { return this.cname; }
return {
parentname:parentname
}
}
plant.prototype.cname = 'plant';
function tomato() {
function parentname() { return this.cname; }
return {
parentname:parentname
}
}
tomato.prototype = new plant(); // <-- settings it to plant as parent
var p = new plant();
var t = new tomato();
console.log(p.parentname()); //object
console.log(t.parentname()); //object
updated code - same result
Object.prototype.cname = 'object';
function plant() {
this.sayparentname = function() { console.log(cname); };
}
plant.prototype.cname = 'plant';
function tomato() {
this.sayparentname = function() { console.log(cname); };
}
tomato.prototype = new plant();
var p = new plant();
var t = new tomato();
p.sayparentname();
t.sayparentname();
Normally a constructor function will modify the object that new creates, with statements such as this.foo = bar, and not return anything. Then the result of the new expression is the object.
However, if the function returns an object, that object will replace the one that new created; so when you use new plant() you're just getting a plain Object instance back.
To fix your code you just need to make it like this:
function Plant() {
function parentName() { return this.cname; }
this.parentName = parentName;
}
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.
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've spent the last couple days researching a way to have private or protected properties in MooTools classes. Various articles (ie, Sean McArthur's Getting Private Variables in a MooTools Class) provide an approach for deprecated versions of MooTools, but I haven't been able to track down a working method for MooTools 1.3+.
Today, after playing with code for hours, I think I have have created a suitable solution. I say "think," because I'm really not that experienced as a programmer. I was hoping the community here could check out my code and tell my if it's actually a valid solution, or a hackjob emulation.
var TestObj = (function() {
var _privateStaticFunction = function() { }
return new Class({
/* closure */
_privates: (function() {
return function(key, val) {
if (typeof(this._data) == 'undefined') this._data = {};
/* if no key specified, return */
if (typeof(key) == 'undefined') return;
/* if no value specified, return _data[key] */
else if (typeof(val) == 'undefined') {
if (typeof(this._data[key]) != 'undefined') return this._data[key];
else return;
}
/* if second argument, set _data[key] = value */
else this._data[key] = val;
}
/* tell mootools to hide function */
})().protect(),
initialize: function() {},
get: function(val) { return this._privates(val); },
set: function(key,val) { this._privates(key,val); }
})
})();
obj1 = new TestObj();
obj2 = new TestObj();
obj1.set('theseShoes','rule');
obj2.set('theseShoes','suck');
obj1.get('theseShoes') // rule
obj2.get('theseShoes') // suck
obj1._privates('theseShoes') // Error: The method "_privates" cannot be called
obj1._privates._data // undefined
obj1._privates.$constructor._data // undefined
I really appreciate any tips! Thanks, everyone!
EDIT: Well, this is embarrassing. I forgot to check out the obvious, obj1._data. I didn't think the this would reference the instance object! So, I suck. Still, any ideas would be awesome!
heh. in your case, a simpler pattern would do the trick.
consider a var behind a closure - extremely hard to puncture. it is available through the getter and setter.
downside: data values cannot be in the instance or they can be accessed directly.
var testObj = (function() {
var data = {__proto__:null}; // 100% private
return new Class({
get: function(key) {
return data[this.uid][key] || null;
},
set: function(key, value) {
data[this.uid][key] = value;
},
remove: function(key) {
delete data[this.uid][key];
},
otherMethod: function() {
alert(this.get("foo"));
},
initialize: function() {
this.uid = String.uniqueID();
data[this.uid] = {};
}
});
})(); // why exec it?
var foo = new testObj();
var bar = new testObj();
foo.set("bar", "banana");
console.log(foo.get("bar")); // banana!
console.log(bar.get("bar")); // undefined.
bar.set("bar", "apple");
console.info(foo.get("bar"), bar.get("bar")); // banana apple
In action: http://jsfiddle.net/dimitar/dCqR7/1/
I am struggling to find a way to puncture this pattern at all - which is sometimes achievable through prototyping like this.
in fact, i played with it some and here's the fixed pattern w/o the namespacing:
http://jsfiddle.net/dimitar/dCqR7/2/
var testObj = (function() {
var data = {__proto__:null}; // 100% private
return new Class({
get: function(key) {
return data[key] || null;
},
set: function(key, value) {
data[key] = value;
},
remove: function(key) {
delete data[key];
},
otherMethod: function() {
alert(this.get("foo"));
}
});
});
var foo = new new testObj();
var bar = new new testObj();
foo.set("bar", "banana");
console.log(foo.get("bar")); // banana!
console.log(bar.get("bar")); // undefined.
bar.set("bar", "apple");
console.info(foo.get("bar"), bar.get("bar")); // banana apple
edit why that is...
my reliance on mootools means my understanding of native js prototypes leaves something to be desired as it abstracts you having to deal with this directly but..
in pattern one, you define AND run the function, which creates the prototype and sets data - a singular instance. you then create new functions with that 'live' prototype where data is already set.
in pattern two, a brand new prototype is created and referenced for each instance, independent of each other. your function returns a new Function with prototype Class... so really new Class({}); hence new new <var>() will create and instantiate the class.
to understand this better, perhaps you can write it like this first - a common enough pattern for creating and instantiating a class that is not being reused - which will make more sense:
new (new Class({
initialize: function() {
alert("hi");
}
}))();
which in turn can be written like this (if saved into a variable):
var foo = new Class({
initialize: function() {
alert("hi");
}
});
new foo();
I hope it makes sense, I am not the best person at explaining...