Ok so I have been trying to figure out a way to make plain old Javascript have some sort of extension inheritance like many other OOP languages. But I have run into a specific problem, when a class extends a parent class using prototype each child of that object share variables rather than have their own new instance of the parent. For example:
TestB.prototype = new TestA();
function TestB(){ }
function TestA(){
var num = 0;
this.count = function(){
num ++;
console.log(num);
}
}
var test = new TestB();
test.count();
var test2 = new TestB();
test2.count();
So what happens is when the code is run the console looks like this:
1
2
Where as what I would prefer is for the "num" variable being inherited from the parent class to be unique to each respective instance and thus the output should be:
1
1
I assume this happens because when prototype is called it only creates a single new instance of TestA rather than one for each time TestB's constructor is called. The problem is that I have not been able to find another way to make it work?
thanks for any help (and note this is actually for a more specific use but I just wanted to create a super simple test case to illustrate the problem cleanly. I do not have the freedom to use an external library such as jQuery or prototype.js to solve the problem)
You are not supposed to define methods in the constructor but in the prototype. This saves memory
, performs better as well as allows the class to be extended cleanly.
function TestA() {
this.num = 0;
}
TestA.prototype = {
count: function() {
console.log( this.num++ );
},
constructor: TestA
};
function TestB(){
TestA.apply( this, arguments ); //Run parent constructor when instantiating a child
}
TestB.prototype = Object.create( TestA.prototype ); //Don't invoke the constructor when merely establishing inheritance
var test = new TestB();
test.count(); //0
var test2 = new TestB();
test2.count(); //0
As far as I know, prototypes only come in to play when reading a property. When you edit a property via the instance and not the prototype, the instance makes it's own property of the same name and the edited value. The prototype value stays the same.
In your example, you carried over a private variable, that is not visible from the outside. Therefore, it can't be "edited" and will carry its value through all instances of the inheriting classes.
In this sample, this makes num public so that it's editable and "patchable" at the instance level.
var test, test2;
function TestA() {
this.num = 0; //public num
this.count = function() {
this.num++;
console.log(this.num);
}
}
function TestB() {} //instances of TestB don't have properties
TestB.prototype = new TestA();
test = new TestB();
test2 = new TestB();
//monitor the instances
console.log(test);
console.log(test2);
test.count(); //these create a num property at the instance level
test2.count(); //but the prototype num remains 0
test2.count();
test2.count(); //test will have 1 and test2 will have 3
Before count operations:
Object -> TypeA -> TypeB -> test
'-> num = 0 '->num = 0 (from TypeA)
'-> count() '->count() (from TypeA)
Object -> TypeA -> TypeB -> test2
'-> num = 0 '->num = 0 (from TypeA)
'-> count() '->count() (from TypeA)
After count operations, prototype num remains 0 and the instance will have num:
Object -> TypeA -> TypeB -> test
'-> num = 0 '->num = 1 (on the instance)
'-> count() '->count() (from TypeA)
Object -> TypeA -> TypeB -> test2
'-> num = 0 '->num = 3 (on the instance)
'-> count() '->count() (from TypeA)
The moment you write to an inherited property of an instance, a new copy of the property is created for that instance.
Fore example, suppose you have:
var objB1 = new TypeB();
var objB2 = new TypeB();
where TypeB inherits the val property from TypeA.
console.log(objB1.val); // reads the inherited value of property val
console.log(objB2.val); // also reads the inherited value of property val
But the moment you write that property, such as:
objB1.val = 35; // objB1 now gets a new copy of the val property.
or
objB1.val++; // objB1 now gets a new copy of the val property.
In both the above cases, when objB1.val is written, it no longer refers to the inherited property but instead a new copy of the property val is created for instance objB1
If you want to store count, one way is to share it; for convenience you can make it a property of the constructor function of your parent class. So the count function would become:
function TestA(){
this.count = function(){
TestA.num++;
console.log(TestA.num);
}
};
TestA.num = 0;
Related
in this code below:
var a = 1;
var boat = { a:2,
innerBoat: {a:3,
print(){console.log(a);},
},
};
boat.innerBoat.print(); //output: 1
i don't understand why console.log(a); in print method returns the value 1.
Also, does the curly braces {} of the object referenced by boat creates a new private execution context in the stack at runtime ? (I know that only function calls create a new private execution context at runtime, but object creation confuses me, because they are originally a constructor call:
var boat = new Object();
i don't understand why console.log(a); in print method returns the value 1.
It logs the value of the variable a.
The properties of the various objects around it which also have the name a are not variables. They can only be referenced as properties of an object.
const a = 1;
const object = {
a: 2
};
console.log(a);
console.log(object.a)
See also How does the “this” keyword work? for how to access the property of an object that the method you are calling is attached to.
Also, does the curly braces {} of the object referenced by boat creates a new private execution context in the stack at runtime ?
No.
In javascript, an object really is nothing more than a collection of data, like a dictionary in many other languages.
var boat = new Object(); just creates an empty object, as if you did var boat = {};
If you're looking for something more like , you may try using ES6 classes
var a = 0
class Boat {
constructor() {
this.a = 1;
}
print() {
console.log(this.a);
}
}
var boat = new Boat()
boat.print(); //output: 2
Okay, so I want to create a constructor function in javascript which will count the total number of instances which are created using this constructor.
var Component = function(name) {
this.name = name;
this.add = function(){this.prototype.countInstances++;};
this.add();
};
and
Component.prototype.countInstances=0;
As I understand it correctly, the countInstances variable is added to the prototype and will act like a static copy for all the instances and will act as my counter.
The problem with this code is that since I'm declaring the countInstances after the constructor, I'm getting an error in the constructor code itself. How to correct this??
If you'd like a property to be attached to the class itself, and not instances of the class, you don't want to add the property to prototype:
var Component = function(name) {
this.name = name;
Component.instanceCount++;
};
Component.instanceCount = 0;
This way, you're assigning each name to its instance, and the total instance count to the static class:
var foo = new Component('bar');
var baz = new Component('qux');
console.info(foo.name, baz.name, Component.instanceCount);
>> 'bar', 'qux', 2
As I understand it correctly, the countInstances variable is added to the prototype and will act like a static copy for all the instances and will act as my counter.
No, it will be, in effect, a default value for instances, not a "static." If you put it on Component.prototype, all instances will inherit it via the prototype chain, but changing it via an instance will give that instance its own copy of it. Example:
var Foo = function() {
};
Foo.prototype.bar = 0;
var f1 = new Foo();
var f2 = new Foo();
console.log(f1.bar, f2.bar); // 0, 0 -- both are still using the `bar` on the prototype
++f1.bar;
console.log(f1.bar, f2.bar); // 1, 0 -- f1 now has its own
Foo.prototype.bar += 2;
console.log(f1.bar, f2.bar); // 1, 2 -- f2 is still using the `bar` on the prototype
The problem with this code is that since I'm declaring the countInstances after the constructor, I'm getting an error in the constructor code itself. How to correct this??
No, the problem is that your instances have no this.prototype object. The prototype property on the function is not copied as a prototype property on instances; it's assigned to them as their prototype, which (somewhat confusingly) isn't called prototype. For a long time it didn't have a name outside the spec at all. You can access it via Object.getPrototypeOf(this) or (this will be standard for browser-based JavaScript as of the next spec) the __proto__ property.
But putting it on the prototype probably doesn't make sense. I'd just use a property on the function itself:
var Component = function(name) {
this.name = name;
this.add = function(){Component.instances++;};
this.add();
};
Component.instances = 0;
But you said you wanted to count the number of objects created by the constructor; the above counts the number of times the add method is called. To count the number of instances created by the constructor, increment it in the constructor:
var Component = function(name) {
Component.instances++;
this.name = name;
this.add = function(){/*Presumably you're doing something here*/};
this.add();
};
Component.instances = 0;
var ComponentCounter = 0;
var Component = function(name) {
this.name = name;
this.add = function(){this.prototype.countInstances++;};
this.add();
ComponentCounter++;
// or Component.counter++;
};
// or add it as a property of Component after it has been defined
// Component.counter = 0;
Variables in the prototype belong to the instance so you have to keep track of that data on a variable that is persisted between instances.
We will be able to do the same by using:
function component() {
if(component.prototype.counter) { component.prototype.counter = 0; }
component.prototype.counter++;
this.add = function(){ /*... do something here....*/ }
}
By initiating counter inside the function body, we will be able to keep the count (number of times function called).
I have the following piece of code:
var Test = function () {
};
Test.prototype.doSomething = function() {
return "done";
};
Now, I create an object of Test
var t = new Test();
alert(t.doSomething()); // Correct alerts "done"
Now I add another method to the prototype:
Test.prototype.fly = function() { return "fly"; };
alert(t.fly()); // Correctly alerts "fly" (existing objects get "live udpated")
Now, I make the prototype point to a blank object:
Test.prototype = {};
alert(t.doSomething()); // Continues to alert "done", but why?
alert(t.fly()); // Continues to alert "fly", but why?
var t2 = new Test();
alert(t.doSomething()); // As expected, this does not work
When I add a method to prototype, it reflects correctly on all new and existing objects
When I "blank" out the prototype by doing <name>.prototype = {};, it only "blanks" out new instances, but not existing ones. Why?
An analogy is this:
var a = {foo : 'bar'};
var b = a; //the same object as a
var c = a;
var d = a;
a.apple = 'orange';
a = 1; //a === 1. b, c and d stay the same, pointing to the object with apple
What I did here is replace what a was pointing, but not the object itself.
When you added fly, you are modifying that single prototype object which all instances share, the object that Test.prototype is currently pointing to.
But when you assigned a blank object to Test.prototype, you modified what Test.prototype was pointing to. It does not modify what the existing instances are pointing to. But from this point on, any new instances will now use the new object on Test.prototype as their prototype object.
If you are familiar with C, I'd rather think of JS variables as pointers rather than references.
I'm completing the previous answer, if you want reflect your changes on all instances you must update the prototype correctly.
Prototype property is an object and you can delete a propery with the reserved keyword 'delete'.
If you want delete 'doSomething' property :
delete Test.prototype.doSomething;
consider
function Foo(){}
Foo.prototype = {a:{"VMAddress":"#1234"}}
consider that Foo.prototype.a object has VMAddress "#1234"
if you create object now then,
var f1 = new Foo();
now f1.a will point to the same object ie with Virtual Machine address "#1234"
if you look
f1.a === Foo.prototype.a ;//prints tue
If you change prototype to some other value now,
Foo.prototype = {a:{"VMAddress":"#5678"}}
and if you create object now then,
var f2 = new Foo();
although
f2.a === Foo.prototype.a; //prints true because both point to same VM address #5678
but
f1.a === f2.a; //prints false
why??
because their VM address are different(one is #1234 and other is #5678) and infact they are different object
final verdict
the prototype chain at the time of object creation decides what an object's prototype will be.
I have such example.
function Rabbit() {
var jumps = "yes";
};
var rabbit = new Rabbit();
alert(rabbit.jumps); // undefined
alert(Rabbit.prototype.constructor); // outputs exactly the code of the function Rabbit();
I want to change the code in Rabbit() so that the var jumps becomes public. I do it this way:
Rabbit.prototype.constructor = function Rabbit() {
this.jumps = "no";
};
alert(Rabbit.prototype.constructor); // again outputs the code of function Rabbit() and with new this.jumps = "no";
var rabbit2 = new Rabbit(); // create new object with new constructor
alert(rabbit2.jumps); // but still outputs undefined
Why is it not possible to change the code in constructor function this way?
You cannot change a constructor by reassigning to prototype.constructor
What is happening is that Rabbit.prototype.constructor is a pointer to the original constructor (function Rabbit(){...}), so that users of the 'class' can detect the constructor from an instance. Therefore, when you try to do:
Rabbit.prototype.constructor = function Rabbit() {
this.jumps = "no";
};
You're only going to affect code that relies on prototype.constructor to dynamically instantiate objects from instances.
When you call new X, the JS engine doesn't reference X.prototype.constructor, it uses the X as the constructor function and X.prototype as the newly created object's prototype., ignoring X.prototype.constructor.
A good way to explain this is to implement the new operator ourselves. ( Crockford will be happy, no more new ;)
// `new` emulator
//
// Doesn't reference `.constructor` to show that prototype.constructor is not used
// when istantiating objects a la `new`
function make(ctorFun, argsArray) {
// New instance attached to the prototype but the constructor
// hasn't been called on it.
const newInstance = Object.create(ctorFun.prototype);
ctorFun.apply(newInstance, argsArray);
return newInstance;
}
// If you create a utility function to create from instance, then it uses the
// inherited `constructor` property and your change would affect that.
function makeFromInstance(instance, argsArray) {
return make(instance.constructor, argsArray);
}
function X(jumps) {
this.jumps = jumps;
}
// Flip the constructor, see what it affects
X.prototype.constructor = function(jumps) {
this.jumps = !jumps;
}
const xFromConstructorIsGood = make(X, [true]);
const xFromInstanceIsBad = makeFromInstance(xFromConstructorIsGood, [true]);
console.log({
xFromConstructorIsGood,
xFromInstanceIsBad
});
Inheritance in JS
Libraries that help with JS inheritance implement inheritance and do rely on prototype.constructor with something in the spirit the following:
function extend(base, sub) {
function surrogateCtor() {}
// Copy the prototype from the base to setup inheritance
surrogateCtor.prototype = base.prototype;
sub.prototype = new surrogateCtor();
// The constructor property is set to the base constructor
// with the above trick, let's fix it
sub.prototype.constructor = sub;
}
You can see that in the above code, we have to fix the constructor property because it's sometimes used to create instantiate an object when you only have an instance. but it doesn't affect the actual constructor. See my post about JS inheritance http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html
How to redefine a constructor
If you really want to redefine a constructor, just do
// If Rabbit had any custom properties on it
// (or static properties as some call it), they would not be copied, you'd have to do that manually using getOwnPropertyNames
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
var oldProto = Rabbit.prototype;
Rabbit = function() {...};
Rabbit.prototype = oldProto;
Note that this would not affect code that had already copied that reference, for example:
const myRefRabbit = Rabbit
This is wonderful workaround creating an object from a literal, not from constructor function.
Firstly, if you want jumps member to be contained in the object, rather than being just a local variable in the constructor then you need this keyword.
function Rabbit() {
this.jumps = "yes";
};
var rabbit = new Rabbit();
alert(rabbit.jumps); // not undefined anymore
And now you can easily now access jumps publicly the way you wanted:
rabbit.jumps = 'no';
alert(rabbit.jumps); // outputs 'no'
But still if you create another Rabbit object it will initially have 'yes' as defined in the constructor, right?
var rabbit2 = new Rabbit();
alert(rabbit.jumps); // outputs 'no' from before
alert(rabbit2.jumps); // outputs 'yes'
What you could do is creating a Rabbit from some default Rabbit Object. The concrete rabbits will always have the default value from default Rabbit object even when you change it on the fly unless you have changed the value in concrete rabbit object (implementation). This is a different than #Juan Mendes's solution which is probably the best but it can open another point of view.
Rabbit = {jumps : 'yes'}; // default object
rabbit = Object.create(Rabbit);
Rabbit.jumps = 'no';
rabbit2 = Object.create(Rabbit);
console.log(rabbit.jumps); // outputs "no" - from default object
console.log(rabbit2.jumps); // outputs "no" - from default object
// but...
rabbit.jumps = 'yes';
Rabbit.jumps = 'unknown';
console.log(rabbit.jumps); // outputs "yes" - from concrete object
console.log(rabbit2.jumps); // outputs "unknown" - from default object
Try the following
function Rabbit() {
this.jumps = "no";
};
var rabbit = new Rabbit();
alert(rabbit.jumps); // Prints "no"
Help,
I have this class
var jMath = {
pi2: Math.PI,
foo: function() {
return this.pi2;
}
}
I want to make the pi2 constant and i want jMath to inherit from Math object. How do I do that?
Oh amusing, scratch all that, this is the correct version:
function JMath() {
this.foo = function() {
return this.PI;
}
}
JMath.prototype = Math;
var jMath = new JMath();
alert(jMath.foo());
(which matches what the other answer is here)
(I originally tried to set the prototype using "JMath.prototype = new Math()" which is how I've seen it other places, but the above works)
Edit
Here's one way to do it as a singleton
// Execute an inline anon function to keep
// symbols out of global scope
var jMath = (function()
{
// Define the JMath "class"
function JMath() {
this.foo = function() {
return this.PI;
}
}
JMath.prototype = Math;
// return singleton
return new JMath();
})();
// test it
alert( jMath.PI );
// prove that JMath no longer exists
alert( JMath );
Consider using prototype:
function JMath() {};
JMath.prototype = {
pi2: Math.PI,
foo: function() {
return this.pi2;
}
}
var j = new JMath();
j.pi2=44; j.foo(); // returns 44
delete j.pi2; j.foo(); // now returns Math.PI
The difference between this and #altCognito's answer is that here the fields of the object are shared and all point to the same things. If you don't use prototypes, you create new and unlinked instances in the constructor. You can override the prototype's value on a per-instance basis, and if you override it and then decide you don't like the override value and want to restore the original, use delete to remove the override which merely "shadows" the prototype's value.
Edit: if you want to inherit all the methods and fields of the Math object itself, but override some things without affecting the Math object, do something like this (change the name "Constructor1" to your liking):
function Constructor1() {};
Constructor1.prototype = Math;
function JMath() {};
JMath.prototype = new Constructor1();
JMath.prototype.pi2 = JMath.prototype.PI;
JMath.prototype.foo = function() { return this.pi2; }
var j = new JMath();
j.cos(j.foo()); // returns -1
edit 3: explanation for the Constructor1 function: This creates the following prototype chain:
j -> JMath.prototype -> Math
j is an instance of JMath. JMath's prototype is an instance of Constructor1. Constructor1's prototype is Math. JMath.prototype is where the overridden stuff "lives". If you're only implementing a few instances of JMath, you could make the overridden stuff be instance variables that are setup by the constructor JMath, and point directly to Math, like #altCognito's answer does. (j is an instance of JMath and JMath's prototype is Math)
There are 2 downsides of augmenting-an-object-in-the-constructor. (Not actually downsides necessarily) One is that declaring instance fields/methods in the constructor creates separate values for each instance. If you create a lot of instances of JMath, each instance's JMath.foo function will be a separate object taking up additional memory. If the JMath.foo function comes from its prototype, then all the instances share one object.
In addition, you can change JMath.prototype.foo after the fact and the instances will update accordingly. If you make the foo function in the constructor as a per-instance method, then once JMath objects are created, they are independent and the only way to change the foo function is by changing each one.
edit 2: as far as read-only properties go, you can't really implement them from within Javascript itself, you need to muck around under the surface. However you can declare so-called "getters" which effectively act as constants:
JMath.prototype.__defineGetter__("pi2", function() { return Math.PI; });
JMath.prototype.__defineSetter__("pi2", function(){}); // NOP
var j = new JMath();
j.pi2 = 77; // gee, that's nice
// (if the setter is not defined, you'll get an exception)
j.pi2; // evaluates as Math.PI by calling the getter function
Warning: The syntax for defining getters/setters apparently is not something that IE doesn't implement nicely.
User-defined object properties can't be constant. Math (and a few other objects) is a special built-in - it has read-only properties and functions. But it's not a constructor - it's just a static object (Math.constructor === Object).
And because JavaScript has prototypal inheritance, and not classical, you can't inherit from Math. (Read more here)
What you can do, however, is define a prototype. When a property isn't found locally, the JS parser looks for that property on the current object's prototype. altCognito's current solutions shows this very well.
I'm curious about exactly what it is you're trying to achieve. Perhaps something like this is what you want?
var jMath = function()
{
const pi2 = Math.PI;
this.getPi2 = function()
{
return pi2;
}
}
var j = new jMath;
alert( j.getPi2() );