Say there is a parent class Shape and a child class Rectangle. I want to reuse a parent class property's value from within the child class.
Can I do this without re-initializing it in the child class (using call or apply)?
I want all the child objects to use the same parent property value.
//Parent
function Shape(ctx) {
this.context = ctx;
}
Shape.prototype.getContext = function() { return this.context; };
//Child - rectangle inherits from shape
function Rectangle(x,y,w,h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
//setup inheritance
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
Rectangle.prototype.draw = function() {
//want to use inherited context here
return this.context;
}
//create and run
var shape = new Shape("value");
var rectangle = new Rectangle(0,0,100,100);
//returns "value"
console.log( shape.getContext() );
//returns undefined - needing "value"
console.log( rectangle.draw() );
EDIT - After the responses below I think this is what I need. Since the rectangle instance is not inheriting from the shape instance the "value" assigned to shape is not being passed to rectangle. How about assigning it as the default inside Shape and then calling the Shape constructor inside the Rectangle constructor. This allows me to share the same context value to all the child objects right?
Side issue, the setter doesn't affect Shape children. So I'm working on that.
//Parent
function Shape() {
this.context = "value";
}
Shape.prototype.getContext = function() { return this.context; };
Shape.prototype.setContext = function(x) { this.context = x; };
//Child - rectangle inherits from shape
function Rectangle(x,y,w,h) {
Shape.call(this);
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
//setup inheritance
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
Rectangle.prototype.draw = function() {
//want to use inherited context here
return this.context;
}
//create and run
var rectangle = new Rectangle(0,0,100,100);
//returns "value"
console.log( rectangle.draw() );
EDIT - Thanks to the responses, I think below is accomplishing what I was initially trying to do. The Shape parent begins with a default context value. The Shape constructor now also accepts an argument in case child classes want to change it when initially called. Each child class then has a getter and setter as well for the context, but it will always default to the initial Parent value unless changed. After looking into it more the Shape is starting to feel like an abstract class or an interface, but that has nothing to do with what I was initially asking.
//Parent
function Shape(ctx) {
this.context = (typeof ctx === "undefined") ? "default" : ctx;
}
Shape.prototype.getContext = function() { return this.context; };
Shape.prototype.setContext = function(x) { this.context = x; };
//Child - rectangle inherits from shape
function Rectangle(x,y,w,h) {
//calls parent constructor
Shape.call(this);
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
//setup inheritance
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
//getter and setter - context defaults to parent value, but can be changed
Rectangle.prototype.getContext = function() { return this.context; };
Rectangle.prototype.setContext = function(x) { this.context = x; };
//other rectangle methods
Rectangle.prototype.draw = function() {
return "doing something with " + this.context;
}
//create and run
var rectangle = new Rectangle(0,0,100,100);
//starts with Parent "default"
console.log( rectangle.getContext() );
//changes and uses different context
rectangle.setContext("different context");
console.log( rectangle.draw() );
Can I do this without re-initializing it in the child class (using call or apply)?
No, there's no way around calling your super constructor.
Notice that this is not re-initialising the property, the call is creating and initialising the property at all.
I want to reuse a parent class property's value from within the child class.
This doesn't really make sense. The property is part of the instance, not of the class. There is no instance of the parent class if you have multiple children, there are just child instances. If you are really creating an instance of the parent class (like in your example code), then that is a separate object and has nothing to do with the other instances - mostly, the children objects are not in any way derived from that parent instance.
The return value is undefined when you do this to setup your rectangle:
//create and run
var shape = new Shape("value");
var rectangle = new Rectangle(0,0,100,100);
because you are actually not connecting that shape to rectangle. You have merely made two separate instances of Shape and Rectangle which aside from sharing certain structures have nothing to do with each other.
There are possible scenarios to fixing this. Create a new Shape to seed the value with up front when defining the prototype.
//Use predefined Shape
var shape = new Shape("value");
Rectangle.prototype = shape;
Now when the previously shown call to draw is used, value will be returned and logged to the console.
This is not a complete scenario in my opinion though because while you have defined a getter for the context, there may be scenarios where you wish to change that. I would also suggest implementing a setter for the context so that you can inject different ones if you feel like it.
Related
In JavaScript: The Good Parts, Crockford argues that one of the downsides of using the pseudo-classical pattern of inheritance is that it publicly exposes instance variables.
For example:
var Ball = function(width, color) {
this.width = width;
this.color = color;
}
var redBall = new Ball(5, "red");
redBall.width = 12; // Changes width to 12
Now, what if I want the width of the ball to be private?
Here's what I've tried:
var Ball = function(width, color) {
this.getWidth = function() { return width; }
this.color = color;
}
var redBall = new Ball(5, "red");
Problem with that is we can still change this.getWidth and there may be prototype methods that rely on it.
What about ...
var Ball = function(width, color) {
return {
getWidth: function() { return width; },
color: color
}
}
var redBall = new Ball(5, "red");
Problem with that is that the prototype methods no longer have access to the instance variables. It's also closer to the functional pattern of inheritance, but with more indirection using the new operator.
So how do I achieve privacy using the pseudo-classical inheritance pattern? Is this even possible?
To answer your question; the only way to have intace specific private members is to have both the members and privileged functions (functions that can access them) in the same scope. This means they all have to be in the constructor body (var my private...this.myPrivileged=function(){ console.log (myPrivate...) or in a IIFE with a closure object keeping track of instances and their privates.
When returning an object private you already loose privacy because the calling code can mutate your private value. To prevent this you have to deep copy the value and return that.
To have privates on the prototype the privates will be shared. This because the instance is not known when declaring your prototype and private members.
This is because JavaScript doesn't have a private modifier and only simulates them through closures.
One pattern that can use prototype for instance specific protected variables is using Crockford's box example.
All protecteds are put in a box that can only be opened with a key, the key is available through closures to all prototype members defined in the IIFE.
Because the instance isn't known when creating prototype you have to invoke initProtecteds from the instance in order to create instance specific protected members.
Minimal code with an example protected instance member called medicalHistory is used in Animal.
function makeBox(key){
var ret = {};
return {
get : function(pKey){
if(pKey===key){
return ret;
}
return false;
}
}
};
var Person = function(args){
args = args || {};
this.name = args.name || "Nameless Person";
this.initProtecteds();
};
//using IIFE to define some members on Person.prototype
// these members and only these members have access to
// the passed object key (through closures)
// later the key is used to create a box for each instance
// all boxes use the same key so instances of same type
// can access each other's protected members and instances
// inheriting from Person can do so too, extending parent methods
// will be trickier, no example for that is given in this code
(function(key){
//private shared member
var privateBehavior = function(instance,args){
//when you invoke this from public members you can pass
// the instance or use call/apply, when using call/apply
// you can refer to this as the current instance, when
// passing it as an argument then instance will
// be the current instance
console.log("private shared invoked");
};
//set default _protecteds to false so init knows
// it has not been initialised and needs to be shadowed
// with a box
Person.prototype._protecteds=false;
Person.prototype.getMedicalHistory = function(){
//Maybe run some code that will check if you can access
// medical history, invoking a private method
privateBehavior(this,{});
var protectedObject = this._protecteds.get(key);
//if medicalHistory is an object the calling code
// can now mutate it
return protectedObject.medicalHistory;
};
Person.prototype.hasSameDesease = function(person){
//this Person instance should be able to see
// medical history of another Person instance
return person._protecteds.get(key);
};
Person.prototype.getArr = function(){
//Returns protecteds.get(key).arr so we can
// mutate it and see if protecteds are instance
// specific
return this._protecteds.get(key).arr;
};
Person.prototype.initProtecteds = function(){
//only create box if it hasn't been created yet
if(this._protecteds!==false)
return;
//use the same key for all instance boxes, one instance
// can now open another instance's box
this._protecteds=makeBox(key);
//retreive the object held by the box
var protectedObject = this._protecteds.get(key);
//add protected members by mutating the object held
// by the box
protectedObject.medicalHistory = "something";
protectedObject.arr = [];
//protectedObject is no longer needed
protectedObject=null;
};
}({}));
var Animal = function(){
this.initProtecteds();
};
(function(key){
Animal.prototype._protecteds=false;
Animal.prototype.initProtecteds = function(){
if(this._protecteds!==false)
return;
this._protecteds=makeBox(key);
var protectedObject = this._protecteds.get(key);
protectedObject.medicalHistory = "something";
};
}({}));
var Employee = function(args){
//re use Person constructor
Person.call(this,args);
};
//set up prototype part of inheritance
Employee.prototype = Object.create(Person.prototype);
//repair prototype.constructor to point to the right function
Employee.prototype.constructor = Employee;
var ben = new Person({name:"Ben"});
var nameless = new Person();
console.log(ben.getMedicalHistory());//=something
//key is in closure and all privileged methods are in that closure
// since {} !== {} you can't open the box unless you're in the closure
// or modify the code/set a breakpoint and set window.key=key in the closure
console.log(ben._protecteds.get({}));//=false
//One Person instance can access another instance's protecteds
// Objects that inherit from Person are same
console.log(ben.hasSameDesease(nameless));//=Object { medicalHistory="something"}
var lady = new Animal();
//An Animal type object cannot access a Person protected members
console.log(ben.hasSameDesease(lady));//=false
var jon = new Employee({name:"Jon"});
console.log(ben.hasSameDesease(jon));//=Object { medicalHistory="something"}
//making sure that protecteds are instance specific
ben.getArr().push("pushed in ben");
console.log(jon.getArr());
console.log(nameless.getArr());
console.log(ben.getArr());
This is interesting to consider.
To me (and I consider myself a student of js), it looks like only private member functions have access to the object's private variables. This is because it creates a closure around the var:
var Ball = function(width, color) {
var width = width;
this.color = color;
this.getWidth=function(){return width}
this.specialWidthCalc=function(x){ width = width + x;}
}
So programmers can do:
var redBall = new Ball(5, "red");
consoloe.log( redBall.getWidth() );
redBall.specialWidthCalc(3);
consoloe.log( redBall.getWidth() );
I am unable to create a prototype which has access to width.
I'm learning prototype based OOP in javascript recently. After reading lots of websites explaining the prototype mechanism in javascript, I settle down with this approach from Mozilla
Here are some snippets from Mozilla:
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info("Shape moved.");
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); // call super constructor.
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
rect instanceof Rectangle // true.
rect instanceof Shape // true.
rect.move(1, 1); // Outputs, "Shape moved."
I understand that Rectangle.prototype = Object.create(Shape.prototype); will set the prototype chain properly so that instance of Rectangle will inherit methods from Shape.
But the Rectangle.prototype.constructor = Rectangle; line is a little bit confusing.
From this website I know that when we create a new object like function MyClass(){}, javascript will set its prototype to an empty object and the MyClass.prototype.constructor will point back to MyClass. So Rectangle.prototype.constructor = Rectangle; will fix the broken link since Object.create(Shape.prototype).constructor still points to Shape.
But if I remove this line, I will still get the following code running without a problem. I'm just feeling curious if there's any use case for the constructor attribute? What will go wrong if I just leave it pointing to the super class?
Setting a property this way will make it enumerable(a for in statement would list the constructor property).
var rec = new Rectangle();
'constructor' in rec; //true
You should be using Object.defineProperty or pass in a second argument to Object.create.
Rectangle.prototype = Object.create(Shape.prototype, {constructor: {enumerable: false }});
var rec = new Rectangle();
'constructor' in rec; //
Please refer to this thread for more on the constructor property:
https://stackoverflow.com/a/4013295
In your code it doesn't make any difference, as you still create your object using Rectangle constructor (explicitly using the name of it: var rect = new Rectangle();).
The difference is only in the case, when you've lost reference to object's constructor, or you simply don't know what is it:
// Shape - superclass
function Shape() {
this.x = 1;
this.y = 1;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info(this.x + " " + this.y);
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); // call super constructor.
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
//Rectangle.prototype.constructor = Rectangle;
console.info("RECT");
var rect = new Rectangle;
console.info(rect);
console.info(rect instanceof Rectangle);
console.info(rect instanceof Shape);
//console.info(rect instanceof Square); TypeError - Square must be a function, but is still not initialized
rect.move(1, 1);
console.info("RECT2");
var rect2 = new rect.constructor();
console.info(rect2);
console.info(rect2 instanceof Rectangle);
console.info(rect2 instanceof Shape);
// console.info(rect2 instanceof Square); TypeError - Square must be a function, but is still not initialized
rect2.move(1, 1);
console.info("RECT3");
var Square = rect.constructor;
Square.prototype.move = function(x, y) { // just to make it different
this.x *= x;
this.y *= y;
console.info(this.x + " " + this.y);
}
var rect3 = new Square();
console.info(rect3);
console.info(rect3 instanceof Rectangle);
console.info(rect3 instanceof Shape);
console.info(rect3 instanceof Square);
rect3.move(4, 4);
I commented out the line with setting the constructor. Uncomment it to see the difference in output. I have also added another way to use the constructor property - another inheritance example (Square based on Shape but with different move method).
So if you don't fix this link, in the case I showed, you will use Shape constructor, and creation of objects of the same type will become simply impossible. rect was of both types, but rect2 is not anymore.
So assuming, even if you don't need it, it is a very good practice to fix this link to the correct constructor, as someone working with your code in the future may need it.
EDIT:
constructor property doesn't affect anything in the object itself. It's set once object is created to save the reference to the function which created the object. If you will need it in the future than it makes sense to have a correct one. I don't see any other issues.
instanceOf will return true if the object is of the specified object type in its inheritance chain.
It has nothing to do with constructor property.
We can use constructor property if we want to initialize property or execute default code at the time of creation of Object.
For example in your example, if we want to create Rectangle always in red color, we can use constructor of Rectangle as follows:
function Rectangle() {
Shape.call(this); // call super constructor.
this.color = "red";
}
Hope this clear your query.
I'm refreshing my javascript skills and came up with a way that objects can inherit from another.
The inheritance should be tree-like.
Parent->Child->Child2
I've extended the Function.prototype (Don't tell me this is a bad idea, this can be changed later)
Function.prototype.extend = function(child)
{
// save child prototype
var childPrototype = child.prototype;
// copy parent to child prototype
child.prototype = Object.create(this.prototype);
// merge child prototype
for (var property in childPrototype) {
child.prototype[property] = childPrototype[property];
}
child.prototype.constructor = child;
child.prototype.$parent = this.prototype;
return child;
};
The parent object:
var Parent = (function()
{
var Parent = function(x, y)
{
this.x = x;
this.y = y;
console.log('Parent constr', x, y);
}
Parent.prototype.move = function(x, y)
{
this.x += x;
this.y += y;
console.log('Parent moved.');
};
return Parent;
}());
First child:
var Child = Parent.extend(function()
{
var Child = function(x, y)
{
this.$parent.constructor(x, y);
console.log('Child constr');
}
Child.prototype.print = function()
{
console.log('Child print', this.x, this.y);
};
// override
Child.prototype.move = function(x, y)
{
this.$parent.move(x, y);
console.log('Child moved.');
};
return Child;
}());
Second child:
var Child2 = Child.extend(function()
{
var Child2 = function(x, y)
{
this.$parent.constructor(x, y);
console.log('Child2 constr');
}
// override
Child2.prototype.move = function(x, y)
{
this.$parent.move(x, y); // call parent move
console.log('Child2 moved.');
};
return Child2;
}());
So far, so good. I'm able to call the parents constructor and methods and even override methods.
var child = new Child2(1, 1);
child.move(1, 1);
child.print();
I get following output which is correct:
Parent constr 1 1
Child constr
Child2 constr
Parent moved.
Child moved.
Child2 moved.
Child print 2 2
But if I comment out the override from second child I get following output:
Parent constr 1 1
Child constr
Child2 constr
Parent moved.
Child moved.
Child moved. -> WHY??
Child print 2 2
I don't understand why Child moved. is outputted twice. The result is correct but something weird is going on.
EDIT:
Finally after researching and diving more into the problem I came with a nice solution:
// call method from parent object
Object.getPrototypeOf(Child2.prototype).move.apply(this, arguments);
I made another extension to Function:
Function.prototype.getParent = function()
{
return Object.getPrototypeOf(this.prototype);
};
And then for example the Child2 move method:
Child2.prototype.move = function(x, y)
{
Child2.getParent().move.call(this, x, y);
};
So I don't need $parent anymore and I get the desired result.
Another solution would be to call the parent prototype directly:
Child2.prototype.move = function(x, y)
{
Child.prototype.move.call(this, x, y);
};
You should be using apply to invoke your functions in the correct scope, also using this to access the parent is not safe. This is actually an excellent problem for learning about JS debugging, you can see everything happening using break points.
looking at your first Child :
// override
Child.prototype.move = function(x, y)
{
this.$parent.move(x, y);
console.log('Child moved.');
};
When this code is reached by calling child.move(1,1), this points to child, and what does child.$parent.move point to? why, the implementation in the first Child of course!
This second time around, this points to the prototype of the Parent, so now we're fine.
So that's why this is happening, what can you do about it? Well that's up to you, I mentioned apply because that was my default thought, so you would use this.$parent.move.apply(this, arguments);, but then we really can't get access to higher parents, so the only other option I can think of is binding (hitching, for any dojo people reading) every function to it's respective prototype, thereby guaranteeing its this value always points to the same thing, and therefore it can always access the $parent reliably.
Of course, this would mean no access to the actual object instance, so it's only really useful for pure utility functions, but this is actually not new. If you had overridden print (which accesses the object instance's x and y members) and tried to invoke the parent, that would have broken as well.
Your extend method attaches the inherited properties and methods to the new object's prototype. It also creates a $parent object which is equal to the prototype of the parent object. Note that this results in duplication. The second child has three move methods:
child.protoype.move
child.$parent.move
child.$parent.$parent.move
Even if you comment out the override there are still three move methods (although two are references to the same function).
Therefore, when you run child.move you get:
child.prototype.move which invokes child.$parent.move which invokes child.$parent.$parent.move
The fact that child.prototype.move and child.$parent.move are both references to the same function does not prevent them from being executed twice.
I am defining a class in JavaScript
function Pen(parent){
this.color = "#0000ff";
this.stroke = 3;
this.oldPt;
this.oldMidPt;
this.isActive = false;
this.parent = parent; //app that owns this pen
this.points = [];
this.curShape;
console.log(this);
return(this);
}
In the console.log statement I am getting way more than just this class, I am getting all kinds of information about basically everything else going on. Why is that?
the keyword this is dependent on the caller, so if you are initializing the function without the "new" keyword "this" might very well be referencing the window and not the object.
Try:
function Pen(parent){
var context = this;
this.color = "#0000ff";
this.stroke = 3;
this.oldPt;
this.oldMidPt;
this.isActive = false;
this.parent = parent; //app that owns this pen
this.points = [];
this.curShape;
console.log(context);
return(this);
}
var pen = new Pen();
Because your "class" inherits (transparently, in your case) from other javascript base classes. If you want to introspect only the properties you've created on your object, use hasOwnProperty(keyName) to filter those out.
Javascript is prototype based and not class based. Every new custom object as default
has pre built functionlity provided by the base object called Object.
So everytime you ask if a property or method belongs to a given object, in the worst
case it will fall until the Object object, and if that property/method is not there
then it will throw an error. Therefore when you are using the log function it is
printing all the object structure including properties and method of its "base" class.
I was looking into the prototype object, and i am a bit confused about the following
//my constructor function
function Circle(r) {
this.radius = r;
this.PI = 3.14;
}
function areaCalculator() {
this.area = this.PI * this.radius * this.radius;
}
function circumferenceCalculator() {
this.circumference = 2* this.PI * this.radius;
}
since our function is an object and has a property called prototype, it is possible to add properties and methods to this prototype object and these would automatically be available for all our custom objects we create using our function constructor.
Circle.prototype.areaCalculator = areaCalculator; //adding function
Circle.prototype.color = "Red"; //adding property
var circle1 = new Circle(5);
circle1.areaCalculator();
console.log(circle1.radius);//5
console.log(circle1.area); //78.5
console.log(circle1.color);//Red
If I understand correctly, all objects using Circle will reference the same color variable unless they are explicitly set. Is this correct?
Also what does it mean to do something like below without using prototype
Circle.circumferenceCalculator = circumferenceCalculator;
Circle.color = "Red";
Are the above two statements correct?
Yes, all the objects created with new Circle will point to the same color property. This property will actually be located on the prototype object, not on the object that you have created. Thus, when you set it on a specific object, it will "shadow" the property from the prototype, but not overwrite it - you can try to delete obj.color after you set it and you'll get the old color back.
Doing Circle.color='red' will simply set the color property on the Circle object (even functions are objects, but they have a callable property which defines their behaviour when called) - this is in no way connected to the Circle's prototype.
A function is also an object and you can augment properties to it. A common example is jQuery, where the $ acts as both an object and a function.
function Circle(r) {}
Circle.circumferenceCalculator = circumferenceCalculator;
Circle.color = "Red";
$.each(); //$ as an object
$('selector').each() //$ as a function
However, this won't reflect in the instance you create. The ones that reflect on the instances are only those that are added via prototype and those in the constructor function.
function Circle(r) {
this.radius = r;
this.PI = 3.14;
}
Circle.prototype.areaCalculator = areaCalculator;
Circle.prototype.color = "Red";
var mycircle = new Circle(5);
//props on Circle itself:
//Circle.color
//Circle.areaCalculator
//props on the instance:
//mycircle.radius
//mycircle.PI
//mycircle.areaCalculator
//mycircle.color