Code example:
ClassA.js
var classB = require('./ClassB');
function ClassA() {
this.ID = undefined;
this.Type = undefined;
....
}
ClassA.prototype.init = function init(id){
this.ID = id;
this.get();
if (this.Type === 'C' && Object.getPrototypeOf(this) === ClassA.prototype) {
return new classB().init(this.ID);
}
}
ClassB.js
function ClassB() {
ClassA.call(this);
this.additionalProp = undefined;
}
ClassB.prototype = Object.create(ClassA.prototype);
ClassB.prototype.constructor = ClassB;
I have implemented two classes ClassA and ClassB.
ClassB is a child of CLassA and has some additional properties.
The prototype chain for ClassB is setup like this:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
The information for the objects is retrieved from an API via an ID.
At the time of the API call I do not know if the object needs to be an instance of ClassA or ClassB. So I always start with an object of ClassA to call the API.
When the API returns a specific Type I want to convert my object from ClassA to more specific ClassB.
I tried to call the constructor of ClassB out of ClassA - this throws an error:
Uncaught TypeError: Object prototype may only be an Object or null: undefined
I don't think I should reference ClassB in ClassA at all, but it was worth a try...
Thank you for helping out a novice! :)
At the time of the API call I do not know if the object needs to be an instance of ClassA or ClassB. So I always start with an object of one class, then when the API returns a specific type I want to convert my object.
Just don't do that. Construct your object after the API returns, and construct it with the correct type in the first place.
I always start with an object of the class to call the API
This is the point where your design has gone wrong. The class should not be responsible for calling the API, it should be responsible only for representing the data and having methods act on the data. Don't start with an empty object that can somehow hydrate itself, have the constructor completely initialise the object from the data passed as parameters.
Put the API call in a separate function, outside of the class. Create a separate class to represent the API endpoint maybe. If absolutely necessary, make it a static method of your base class, it still can create new ClassA or new ClassB instances then.
In a comment you've mentioned this error:
Uncaught TypeError: Object prototype may only be an Object or null: undefined
You get that because ClassA.js and ClassB.js have a circular relationship: each tries to import something the other exports. That can be fine in some cases, but here you have code in ClassB.js trying to use ClassA.prototype in top-level code, and ClassA.js importing ClassB from ClassB.js. You end up with a placeholder for the ClassA import and the Object.create(ClassA.prototype) call doesn't work.
It's going to be much easier if you define both of them in the same file, thanks to function declaration hoisting.
I'd also modify init so that it always returns the instance, since you need to return a new object sometimes but not other times. So have it always return the instance simplifies the calling code.
Here's a minimal-changes example:
function ClassA() {
this.ID = undefined;
this.Type = undefined;
// ....
}
ClassA.prototype.get = function () {
// *** Just for debugging, ID 2 is `ClassB`, any other is `ClassA`
this.Type = this.ID === 2 ? "C" : "X";
};
ClassA.prototype.init = function init(id) {
this.ID = id;
this.get();
if (this.Type === "C" && Object.getPrototypeOf(this) === ClassA.prototype) {
return new ClassB().init(id); // *** Use `ClassB`, not `ClassA`
}
return this; // *** So the caller always gets an instance they can use
};
function ClassB() {
ClassA.call(this);
this.additionalProp = undefined;
}
ClassB.prototype = Object.create(ClassA.prototype);
ClassB.prototype.constructor = ClassB;
module.exports.ClassA = ClassA;
module.exports.ClassB = ClassB;
Then using it (just for example):
var both = require("./both");
var ClassA = both.ClassA;
var ClassB = both.ClassB;
var obj1 = new ClassA();
obj1 = obj1.init(1);
console.log(obj1 instanceof ClassA); // true
console.log(obj1 instanceof ClassB); // false
var obj2 = new ClassA();
obj2 = obj2.init(2);
console.log(obj2 instanceof ClassA); // true
console.log(obj2 instanceof ClassB); // true
That said, I think I'd refactor this. You've said that there's a separate init method because sometimes you want to use methods on the objects before you have an id. That makes me think ClassA (at least) is trying to do too much, both things that it can do when it doesn't know what it is (no id) and things it can do when it does. The instance returned by a constructor should be fully baked and ready to go. So probably better to split the parts of ClassA that don't need an id off into something else. That would also mean that ClassA didn't have to refer to ClassB, which isn't best practice.
I think I'd probably also split get off to be separate from the classes, and have it return the appropriate instance.
For example:
ClassA.js:
function ClassA(data) {
this.ID = data.id;
this.Type = data.type;
// ....
}
// ...other `ClassA` methods...
module.exports = ClassA;
ClassB.js:
var ClassA = require("./ClassA");
function ClassB(data) {
ClassA.call(this, data);
this.additionalProp = data.additionalProp;
}
ClassB.prototype = Object.create(ClassA.prototype);
ClassB.prototype.constructor = ClassB;
// ...other `ClassB` methods...
module.exports = ClassB;
get.js (or whatever):
var ClassA = require("./ClassA");
var ClassB = require("./ClassB");
function get(id) {
var data = /*...get from API...*/;
var cls = "additionalData" in data ? ClassB : ClassA;
return new cls(data);
}
That provides a much better separation of concerns.
Related
I have a library of javascript classes that inherit from each other using John Resig's simple Javascript inheritance library (http://ejohn.org/blog/simple-javascript-inheritance/)
One of my classes (let's call it parent) has a number of child classes (parent.child1, parent.child2, etc) that extend the parent class and add their own methods.
Normally, I would instatiate a child object by
ob=new parent.child1(ops)
However, when I instantiate a parent object, the creation options hash can include a member called type, which tells me that this object must actually be a child object.
ob= new parent({type:"child1"});
During the object creation, how can I make sure that the object created is an instance of parent.child1? If that is not possible, how can I make sure that at least the created object has all the methods and properties of parent.child1.prototype?
Thanks in advance
EDIT: A better example is to have a class called employee and then two classes that inherit from employee called employee.engineer and employee.admin
function Parent(options) {
if(options.type) return new Parent[options.type]();
}
Parent.prototype.foo = 'foo';
Parent.Child = function() {
this.bar = 'bar';
};
Parent.Child.prototype = Object.create(Parent.prototype);
Parent.Child.prototype.constructor = Parent.Child;
var obj = new Parent({type:"Child"});
obj instanceof Parent; // true
obj instanceof Parent.Child; // true
obj.constructor; // Parent.Child
obj.foo; // 'foo'
obj.bar; // 'bar';
Try something like this...
var ob = new parent[type]( ops );
Also, read about factory methods, or the factory pattern:
http://addyosmani.com/resources/essentialjsdesignpatterns/book/#factorypatternjavascript
Thanks to Oriol's suggestion, I came up with the solution:
This is the part of John Resig's library that corresponds to the class' constructor:
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
I modified this slightly:
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init ){
var ret=this.init.apply(this, arguments);
if(ret) return ret;
}
}
Now in my employee class init method I start with:
init:function(ops){
if(ops.type)
return new employee[ops.type];
//rest of init method
}
I have a javascript scenario where I have created a base class and a derived class and wish to pack the total set of properties into a JSON-string with JSON.stringify().
When I use the equivalent to the code below I only get the child-object's properties when I run "toString()" on one of the DerivedClass instances:
function BaseClass() {
this.version = "0.0.0";
this.time = Date.now();
this.type = this.constructor.name;
}
BaseClass.prototype.BaseClassException = function(message) {
this.message = message;
}
BaseClass.prototype.toString = function() {
return JSON.stringify(this);
}
BaseClass.parse = function(jsonString) {
var json = JSON.parse(jsonString);
switch(json.type) {
case "DerivedClass1":
return new DerivedClass1();
case "DerivedClass2":
return new DerivedClass2();
default:
throw new BaseClassException("No compatible type found when parsing: " + jsonString);
}
function DerivedClass1(prop1, prop2) {
this.prop1 = prop1;
this.prop2 = prop2;
this.type = this.constructor.name;
}
DerivedClass1.prototype = new BaseClass();
DerivedClass1.prototype.constructor = DerivedClass1;
function DerivedClass2(prop3) {
this.prop3 = prop3;
}
DerivedClass2.prototype = new BaseClass();
DerivedClass2.prototype.constructor = DerivedClass2;
// Test
var dc1 = new DerivedClass1("A", "B");
console.log(dc1.toString()); // Returns JSON-string with properties of DerivedClass1, but not BaseClass
There will be several different derived classes. While I do know that js doesn't really support classes I would still like to pack all the properties from the base and child objects in the same JSON-string. The structure is necessary to correlate to the other nodes of the total system, ie all properties need to be present.
If anyone at the same time has the knowledge of nudging me in the correct direction to understand the link between the child object and parent object in order for me to better understand the "inheritance" part of js I'd be really thankful as well. I more used to strict oo-languages so I'd be happy to learn.
There are two things which I can readily suggest.
To invoke the base class constructor, you have to invoke it manually like this
function DerivedClass1(prop1, prop2) {
BaseClass.call(this);
this.prop1 = prop1;
this.prop2 = prop2;
this.type = this.constructor.name;
}
We invoke the parent constructor function, with the current object. The important thing to note here is that, we are setting the current context to the object of type DerivedClass1.
To actually do prototypal inheritance, you need to use the base class's prototype, not the object.
DerivedClass1.prototype = Object.create(BaseClass.prototype);
In your case, BaseClass's constructor doesn't depend on any arguments. So, doing DerivedClass1.prototype = new BaseClass(); will not make a big difference. But it is always better to depend only on the Parent constructor's prototype. Read more about using Object.create for inheritance, in this wonderful answer.
I have been trying to learn OOP with JavaScript before I start attempting to learn backbone.js.
I want to be able to data bind but I can't seem to get it to work.
I've just made a simple protoype of a budget website that you can put in a budget and input how much you've spent, and it will show if you've gone over.
function BudgetItem(spent, budget){
this.setSpent = function(spent){
this.spent = spent;
}
this.setBudget = function(budget){
this.budget = budget;
}
this.getSpent = function(){
return this.spent;
}
this.getBudget = function(){
return this.budget;
}
}
function BudgetType(type){
this.getType = function(){
return type;
}
}
BudgetType.prototype = new BudgetItem();
$(document).ready(function(){
var food = new BudgetType('food');
$('.budget').html(food.getBudget());
$('.editbudget').change(function(){
food.setBudget($('.editbudget').data())
});
})
That's my code thus far. I'm not sure if I'm doing it right. Am I supposed to extend things? Also, can someone explain how to dynamically data bind without a library?
First I'll give you some theory. A Javascript function is a dynamic object, just like Object is, and a new instance can be created using the new keyword much like you are doing in your listener. When this happens, the function itself will run as a constructor while the this keyword will be bound to the newly created object. What you're doing above then is in fact adding new properties on the fly as you're passing in their values for the first time... which is fine, but not very clear to another reader.
Now for the tricky part. Every function has a link to a "hidden" Prototype object. This is an anonymous (not accessible by name) object created by the JavaScript runtime and passed as a reference to the user object through the prototype property. This Prototype object also has a reference to the function through its constructor property. To test what I'm saying for yourself, try the following:
BudgetItem.prototype.constructor === BudgetItem // true
Putting it all together, you can now think of functions as constructors to (hidden) classes that are created for you behind the scenes, accessible through the function's prototype property. So, you could add the fields to the Prototype object directly as so:
function BudgetItem(spent) {
this.spent = spent
}
BudgetItem.prototype.setSpent = function(spent) { this.spent = spent };
BudgetItem.prototype.getSpent = function() { return this.spent };
Another problem is inheritance and passing parameters to the constructor. Again, your version is valid but you lose the ability to pass the spent and budget values when initializing a BudgetType. What I would do is forget prototypes and go:
function BudgetType(type, spent) {
var instance = new BudgetItem(spent);
instance.type = type;
return instance;
}
This is close to what Scott Sauyet suggested above but more powerful. Now you can pass both parameters (and more) and have a more complicated inheritance tree.
Finally, what you can do is create private (or pseudo-private, more accurately) properties by providing a getter to an otherwise automatic variable (one passed as an argument or initialised inside the function). This is a special feature of the language and it works like so:
function BudgetType(type, spent) {
var instance = new BudgetItem(spent);
instance.getType = function() {
return type;
}
return instance;
}
Now you can access the 'type' passed in the constructor by obj.getType() but cannot override the initial value. Even if you define obj.type = 'New Value' the getType() will return the initial parameter passed because it has a reference to another context which was created when the object was initialised and never got released due to the closure.
Hope that helps...
if you want all instances of objects to reference the same members/values you can use a closure:
// create a constrctor for you object wrapped in a closure
myCon = (function() {
// define shared members up here
var mySharedObj = new function () {
this.member = "a";
}();
// return the actual constructor
return function () {
this.mySharedObj = mySharedObj;
}
}());
// create two instances of the object
var a = new myCon();
var b = new myCon();
// Altering the shared object from one
a.mySharedObj.member = "b";
// Alters it for all
console.log(b.mySharedObj.member);
If you want to build objects from other objects(sort of like other languages' class whatever extends baseClass), but do not want them to share values via reference(instead a clone of values), you can use something like the following:
Object.prototype.extendsUpon = (function (_prop, _args) {
return function (base) {
for (var key in base) {
if (_prop.call(base, key)) {
this[key] = base[key];
}
}
function con(child){
this.constructor = child;
}
con.prototype = base.prototype;
this.prototype = new con(this);
this.__base__ = base.prototype;
var args = _args.call(arguments);
args.shift();
base.constructor.apply(this, args);
}
}(Object.prototype.hasOwnProperty, Array.prototype.slice));
Then to build objects ontop of objects:
// Base Object Constructor
function Fruit(name) {
this.fruitname = name;
}
Fruit.prototype.yum = function() {
return "I had an " + this.fruitname;
}
// Object constructor that derives from the Base Object
function Favorite() {
// Derive this object from a specified base object:
// #arg0 -> Object Constructor to use as base
// #arg1+ -> arguments passed to the BaseObject's constructor
this.extendsUpon(Fruit, "apple");
// From here proceed as usual
// To access members from the base object that have been over-written,
// use "this.__base__.MEMBER.apply(this, arguments)"
}
Favorite.prototype.yum = function() {
return this.__base__.yum.apply(this) + " and it was my favorite";
}
var mmm = new Favorite();
// Outputs: "I had an apple and it was my favorite"
mmm.yum();
I've spent the last couple of hours trying to find a solution to my problem but it seems to be hopeless.
Basically I need to know how to call a parent method from a child class.
All the stuff that I've tried so far ends up in either not working or over-writing the parent method.
I am using the following code to set up OOP in javascript:
// SET UP OOP
// surrogate constructor (empty function)
function surrogateCtor() {}
function extend(base, sub) {
// copy the prototype from the base to setup inheritance
surrogateCtor.prototype = base.prototype;
sub.prototype = new surrogateCtor();
sub.prototype.constructor = sub;
}
// parent class
function ParentObject(name) {
this.name = name;
}
// parent's methods
ParentObject.prototype = {
myMethod: function(arg) {
this.name = arg;
}
}
// child
function ChildObject(name) {
// call the parent's constructor
ParentObject.call(this, name);
this.myMethod = function(arg) {
// HOW DO I CALL THE PARENT METHOD HERE?
// do stuff
}
}
// setup the prototype chain
extend(ParentObject, ChildObject);
I need to call the parent's method first and then add some more stuff to it in the child class.
In most OOP languages that would be as simple as calling parent.myMethod()
But I really cant grasp how its done in javascript.
Any help is much appreciated, thank you!
ES6 style allows you to use new features, such as super keyword. super keyword it's all about parent class context, when you are using ES6 classes syntax. As a very simple example, checkout:
Remember: We cannot invoke parent static methods via super keyword inside an instance method. Calling method should also be static.
Invocation of static method via instance method - TypeError !
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
classMethod() {
return super.classMethod() + ', too';
}
}
console.log(Bar.classMethod()); // 'hello' - Invokes inherited static method
console.log((new Bar()).classMethod()); // 'Uncaught TypeError' - Invokes on instance method
Invocation of static method via super - This works!
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
static classMethod() {
return super.classMethod() + ', too';
}
}
console.log(Bar.classMethod()); // 'hello, too'
Now super context changes based on invocation - Voila!
class Foo {
static classMethod() {
return 'hello i am static only';
}
classMethod() {
return 'hello there i am an instance ';
}
}
class Bar extends Foo {
classMethod() {
return super.classMethod() + ', too';
}
}
console.log((new Bar()).classMethod()); // "hello there i am an instance , too"
console.log(Bar.classMethod()); // "hello i am static only"
Also, you can use super to call parent constructor:
class Foo {}
class Bar extends Foo {
constructor(num) {
let tmp = num * 2; // OK
this.num = num; // ReferenceError
super();
this.num = num; // OK
}
}
And of course you can use it to access parent class properties super.prop.
So, use ES6 and be happy.
Here's how its done: ParentClass.prototype.myMethod();
Or if you want to call it in the context of the current instance, you can do:
ParentClass.prototype.myMethod.call(this)
Same goes for calling a parent method from child class with arguments:
ParentClass.prototype.myMethod.call(this, arg1, arg2, ..) * Hint: use apply() instead of call() to pass arguments as an array.
Well in order to do this, you are not limited with the Class abstraction of ES6. Accessing the parent constructor's prototype methods is possible through the __proto__ property (I am pretty sure there will be fellow JS coders to complain that it's depreciated) which is depreciated but at the same time discovered that it is actually an essential tool for sub-classing needs (especially for the Array sub-classing needs though). So while the __proto__ property is still available in all major JS engines that i know, ES6 introduced the Object.getPrototypeOf() functionality on top of it. The super() tool in the Class abstraction is a syntactical sugar of this.
So in case you don't have access to the parent constructor's name and don't want to use the Class abstraction you may still do as follows;
function ChildObject(name) {
// call the parent's constructor
ParentObject.call(this, name);
this.myMethod = function(arg) {
//this.__proto__.__proto__.myMethod.call(this,arg);
Object.getPrototypeOf(Object.getPrototypeOf(this)).myMethod.call(this,arg);
}
}
Here's a nice way for child objects to have access to parent properties and methods using JavaScript's prototype chain, and it's compatible with Internet Explorer. JavaScript searches the prototype chain for methods and we want the child’s prototype chain to looks like this:
Child instance -> Child’s prototype (with Child methods) -> Parent’s prototype (with Parent methods) -> Object prototype -> null
The child methods can also call shadowed parent methods, as shown at the three asterisks *** below.
Here’s how:
//Parent constructor
function ParentConstructor(firstName){
//add parent properties:
this.parentProperty = firstName;
}
//add 2 Parent methods:
ParentConstructor.prototype.parentMethod = function(argument){
console.log(
"Parent says: argument=" + argument +
", parentProperty=" + this.parentProperty +
", childProperty=" + this.childProperty
);
};
ParentConstructor.prototype.commonMethod = function(argument){
console.log("Hello from Parent! argument=" + argument);
};
//Child constructor
function ChildConstructor(firstName, lastName){
//first add parent's properties
ParentConstructor.call(this, firstName);
//now add child's properties:
this.childProperty = lastName;
}
//insert Parent's methods into Child's prototype chain
var rCopyParentProto = Object.create(ParentConstructor.prototype);
rCopyParentProto.constructor = ChildConstructor;
ChildConstructor.prototype = rCopyParentProto;
//add 2 Child methods:
ChildConstructor.prototype.childMethod = function(argument){
console.log(
"Child says: argument=" + argument +
", parentProperty=" + this.parentProperty +
", childProperty=" + this.childProperty
);
};
ChildConstructor.prototype.commonMethod = function(argument){
console.log("Hello from Child! argument=" + argument);
// *** call Parent's version of common method
ParentConstructor.prototype.commonMethod(argument);
};
//create an instance of Child
var child_1 = new ChildConstructor('Albert', 'Einstein');
//call Child method
child_1.childMethod('do child method');
//call Parent method
child_1.parentMethod('do parent method');
//call common method
child_1.commonMethod('do common method');
In case of multiple inheritance level, this function can be used as a super() method in other languages. Here is a demo fiddle, with some tests, you can use it like this, inside your method use : call_base(this, 'method_name', arguments);
It make use of quite recent ES functions, an compatibility with older browsers is not guarantee. Tested in IE11, FF29, CH35.
/**
* Call super method of the given object and method.
* This function create a temporary variable called "_call_base_reference",
* to inspect whole inheritance linage. It will be deleted at the end of inspection.
*
* Usage : Inside your method use call_base(this, 'method_name', arguments);
*
* #param {object} object The owner object of the method and inheritance linage
* #param {string} method The name of the super method to find.
* #param {array} args The calls arguments, basically use the "arguments" special variable.
* #returns {*} The data returned from the super method.
*/
function call_base(object, method, args) {
// We get base object, first time it will be passed object,
// but in case of multiple inheritance, it will be instance of parent objects.
var base = object.hasOwnProperty('_call_base_reference') ? object._call_base_reference : object,
// We get matching method, from current object,
// this is a reference to define super method.
object_current_method = base[method],
// Temp object wo receive method definition.
descriptor = null,
// We define super function after founding current position.
is_super = false,
// Contain output data.
output = null;
while (base !== undefined) {
// Get method info
descriptor = Object.getOwnPropertyDescriptor(base, method);
if (descriptor !== undefined) {
// We search for current object method to define inherited part of chain.
if (descriptor.value === object_current_method) {
// Further loops will be considered as inherited function.
is_super = true;
}
// We already have found current object method.
else if (is_super === true) {
// We need to pass original object to apply() as first argument,
// this allow to keep original instance definition along all method
// inheritance. But we also need to save reference to "base" who
// contain parent class, it will be used into this function startup
// to begin at the right chain position.
object._call_base_reference = base;
// Apply super method.
output = descriptor.value.apply(object, args);
// Property have been used into super function if another
// call_base() is launched. Reference is not useful anymore.
delete object._call_base_reference;
// Job is done.
return output;
}
}
// Iterate to the next parent inherited.
base = Object.getPrototypeOf(base);
}
}
How about something based on Douglas Crockford idea:
function Shape(){}
Shape.prototype.name = 'Shape';
Shape.prototype.toString = function(){
return this.constructor.parent
? this.constructor.parent.toString() + ',' + this.name
: this.name;
};
function TwoDShape(){}
var F = function(){};
F.prototype = Shape.prototype;
TwoDShape.prototype = new F();
TwoDShape.prototype.constructor = TwoDShape;
TwoDShape.parent = Shape.prototype;
TwoDShape.prototype.name = '2D Shape';
var my = new TwoDShape();
console.log(my.toString()); ===> Shape,2D Shape
There is a much easier and more compact solution for multilevel prototype lookup, but it requires Proxy support. Usage: SUPER(<instance>).<method>(<args>), for example, assuming two classes A and B extends A with method m: SUPER(new B).m().
function SUPER(instance) {
return new Proxy(instance, {
get(target, prop) {
return Object.getPrototypeOf(Object.getPrototypeOf(target))[prop].bind(target);
}
});
}
more flexible answer with classic js.
You define "_parent = A.prototype;" in the child class, then you can call parent's methods with apply:
class A{
_msg='A';
_msgOnlyA=' great from A';
constructor(){
}
hello(){
console.log('hello '+this._msg+', '+this._msgOnlyA);
}
};
class B extends A{
_parent = A.prototype;
_msg='B';
constructor(){
super();
}
hello(){
this._parent.hello.apply(this, arguments);
console.log('hello '+this._msg);
}
};
var b = new B();
b.hello();
While you can call the parent method by the prototype of the parent, you will need to pass the current child instance for using call, apply, or bind method. The bind method will create a new function so I doesn't recommend that if you care for performance except it only called once.
As an alternative you can replace the child method and put the parent method on the instance while calling the original child method.
function proxy(context, parent){
var proto = parent.prototype;
var list = Object.getOwnPropertyNames(proto);
for(var i=0; i < list.length; i++){
var key = list[i];
// Create only when child have similar method name
if(context[key] !== proto[key]){
let currentMethod = context[key];
let parentMethod = proto[key];
context[key] = function(){
context.super = parentMethod;
return currentMethod.apply(context, arguments);
}
}
}
}
// ========= The usage would be like this ==========
class Parent {
first = "Home";
constructor(){
console.log('Parent created');
}
add(arg){
return this.first + ", Parent "+arg;
}
}
class Child extends Parent{
constructor(b){
super();
proxy(this, Parent);
console.log('Child created');
}
// Comment this to call method from parent only
add(arg){
return super.add(arg) + ", Child "+arg;
}
}
var family = new Child();
console.log(family.add('B'));
// Base class
var Base = function() {
this._value = 'base';
};
Base.prototype = {
constructor: Base,
// By function
getValue: function() {
return this._value;
},
// By getter
get value() {
return this._value;
}
};
// Sub class extends Base
var Sub = function() {
this._value = 'sub';
};
Sub.prototype = {
constructor: Sub
};
// Pass over methods
Sub.prototype.getValue = Base.prototype.getValue;
Sub.prototype.value = Base.prototype.value;
// ---
var mySub = new Sub();
alert(mySub.getValue()); // Returns 'sub'
alert(mySub.value); // Returns 'undefined'
At first glance it seems that mySub.value should return the same as mySub.getValue(), but as you can see it instead returns undefined. Obviously the getter is not finding the parent scope as the Sub instance (mySub), but rather a non-existent Base instance.
Is there any way around this other than having to assign the same getters onto the new prototype?
A more modern solution is to use the Object.defineProperty since it allows getters and setters to be handled without breaking them.
Only problem is that it takes a descriptor object, so instead of manually making one, use the Object.getOwnPropertyDescriptor function to just get it for you.
var BazValue = Object.getOwnPropertyDescriptor(Base.prototype,'value');
Object.defineProperty(Sub.prototype, 'value', BazValue);
Sub.prototype.__defineGetter__('value', Base.prototype.__lookupGetter__('value'));
Try that.
I think it would work if you assigned
Sub.prototype = new Base()
The issue is that the constructor is never run when you assign it directly from the Base.prototype.value. That value won't exist until you have an instance of the Base class (via new)
This is my typical method for extending Function to achieve inheritance:
Function.prototype.Extend = function(superClass) {
this.prototype = new superClass();
this.prototype.getSuperClass = function() {
return superClass;
};
this.getSuperClass = this.prototype.getSuperClass;
return this;
};
This will properly assign all of the parent classes methods and properties to the child 'class'.
Usage looks like
var Sub = function() {}
Sub.Extend(Base)
In addition to Alex Mcp's answer you could add new getters/setters to Sub after extending it using:
Function.prototype.addGetter = function(val,fn){
this.prototype.__defineGetter__(val,fn);
return this;
}
Function.prototype.addSetter = function(val,fn){
this.prototype.__defineSetter__(val,fn);
return this;
}
//example;
Sub.Extend(Base);
Sub.addGetter('date',function(){return +new Date;});
And to add to tylermwashburns answer: you could extend the Function prototype for that:
Function.prototype.copyGetterFrom = function(val,fromConstructor){
this.prototype.__defineGetter__(
val
,fromConstructor.prototype.__lookupGetter__(val));
return this;
}
//usage example.:
Sub.copyGetterFrom('value',Base);