I'm trying to understand how inheritance works in JS. Suppose we have a class:
Class = function () {
this.A = 'A';
this.B = 'B';
};
and we are trying to extend it
SubClass = function () {};
SubClass.prototype = new Class();
Do I understance correctly that after inheritance properties A and B are common for all instances of SubClass, since they belong to it's prototype? If yes, how can Class be extended so that A and B do not be part of prototype?
UPD: note that Class uses A and B, so I can't declare them in SubClass.
Thank you in advance!
All I want is to make A and B be accessible and specific for each
"instance"
The typical way of doing this is to pass parameters and assign them to properties. Then you can use call to reference the super class. In other words:
function Person( name, age ) {
this.name = name;
this.age = age;
}
function Student( name, age, grade ) {
Person.call( this, name, age ); // call super-class with sub-class properties
this.grade = grade;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
var roger = new Student( 'Roger', 18, 'A+' );
You can use properties in parent class without defining:
Class = function () {
this.sum = function() {
return this.a+this.b;
}
};
SubClass = function () {
this.a = 5;
this.b = 6;
};
SubClass.prototype = new Class();
var z = new SubClass();
z.sum(); //11
Another way: Create function in prototype which creates your properties:
Class = function () {
this.makeAB = function() { //called with context of SubClass
this.A = 'A';
this.B = 'B';
}
};
SubClass = function () { this.makeAB() };
SubClass.prototype = new Class();
var z = new SubClass();
z.A = 'AAA';
z.B = 'BBB';
var z2 = new SubClass();
console.log(z)
console.log(z2)
Related
I've put together a simplified example of what I'm trying to do, obviously a bit contrived... I have this class:
export class myClass {
a = 'bar';
b = 0;
save(x: any = null): void {
//save all properties
//...
}
}
In other classes that need to use it, I will define foo = new myClass();
Then it can be used either as:
this.foo.b = 3
this.foo.save();
or, because sometimes I just want it on one line (hence the x: any = null:
this.foo.save(this.foo.b = 3);
I would like to write the single line version more elegantly, and feel something like this should be possible... is it?
//How can I make this possible?
this.foo.save(c => c.b = 3)
if it is possible, what would the add method look like?
Many thanks!
Answer for the original question.
If you want this.calc.add(c => c.b = 3), then you need to handle invoking the function c => c.b = 3 once passed to the add method.
So just check the value is a function, if it is then pass this to the function, which would be c in your function, then the return value you add with this.b
Plain old js.
class Calculator {
constructor() {
this.a = 10
this.b = 0
this.sum = 0
}
add(x) {
this.sum = this.a + (typeof x === 'function' ? x(this) : x)
}
}
const calc = new Calculator()
calc.add(c => c.b = 3)
console.log(calc.sum)
calc.add(1)
console.log(calc.sum)
Implicitly assigning is anti pattern
// Something that you should avoid
this.calc.b = 3
class Calc {
constructor(private a: number = 0, private b: number = 0) {}
setA(a: number) {
this.a = a;
return this;
}
setB(b: number) {
this.b = b;
return this;
}
sum() {
return this.a + this.b;
}
}
const calc = new Calc();
// will return 0
console.log(calc.sum());
// will return 6
console.log(calc.setA(1).setB(5).sum());
const calc1 = new Calc(1,2);
// will return 3
console.log(calc1.sum());
let construct = function(name = "john", last = "marks") {
return {
name: name,
last: last,
};
};
let me = new construct("mike", "tyson");
console.log(me);
You do not have a constructor function.
A constructor function is one you can call with the keyword new in order to produce a new instance of said function. However, yours that just produces plain objects with no inheritance:
Your implementation:
let construct = function(name = "john", last = "marks") {
return {
name: name,
last: last,
};
};
let me = new construct("mike", "tyson");
console.log(me instanceof construct); //false
Proper constructor function:
let construct = function(name = "john", last = "marks") {
this.name = name;
this.last = last;
};
let me = new construct("mike", "tyson");
console.log(me instanceof construct); //true
This matters if you want to use prototypal inheritance for the produced instances:
let construct = function(name = "john", last = "marks") {
this.name = name;
this.last = last;
};
let me = new construct("mike", "tyson");
construct.prototype.fullName = function() {
return `${this.name} ${this.last}`;
}
console.log(me.fullName()); //"mike tyson"
In order to produce proper instances with new, it needs to either return nothing (which implicitly produces undefined) or return anything other than an object:
function example() {
this.foo = "bar";
return "this is not an object";
}
console.log(example());
console.log(new example());
Consider the following scenario:
class A {
constructor() {
this.A = 'A';
}
createB() {
//Create a B instance from this current instance
}
}
class B extends A {
constructor() {
super();
this.B = 'B';
}
}
var base = new A();
var derived = new B();
base.A = 'C';
// Create a B instance from A so that
// a new A isn't instantiated and
// fromInstance.A === 'C'
var fromInstance = base.createB();
I would like to be able to create an instance of B without having to create a new instance of A, but rather use the existing A instance.
My goal is to be able to spawn a B instance by calling a function within A, but also allow a B instance to be created directly and handle constructing a default A.
How can I achieve something like this when B extends A and requires super() to be called?
Not sure this is exactly what you want but it works for your example:
class A {
constructor() {
this.A = 'A';
}
}
class B extends A {
constructor() {
super();
this.B = 'B';
}
}
var base = new A();
var derived = new B();
base.A = 'C';
// Create a B instance from A so that
// a new A isn't instantiated and
// fromInstance.A === 'C'
var fromInstance = new B();
Object.assign(fromInstance, base);
console.log(fromInstance);
Here is an alternate solution. It is actually pretty common in C# and Java, but since JS has no method overloading, this is kind of cumbersome and not so nice compared to the above solution:
class A {
constructor(source) {
if(source){
//use properties/stuff from source
this.A=source.A;
}
else{
//only perform initialization if source is not provided
this.A = 'A';
}
}
}
class B extends A {
constructor(source) {
super(source);
this.B = 'B';
}
}
var base = new A();
var derived = new B();
base.A = 'C';
// Create a B instance from A so that
// a new A isn't instantiated and
// fromInstance.A === 'C'
var fromInstance = new B(base);
console.log(fromInstance);
Basically, there are two versions of the constructor, one that creates a completely new object, and one that pretty much copies an old object.
I think there is a bit of a misunderstanding, every instance of B is by definition an instance of A, no matter what you do. If you want super to be called, you are calling the constructor of A, and thus "instantiating" A.
If I understand the problem, you want the new instance's A property to reflect the A property of the instance that created it, right? You can set this in createB since it will be called on A instance. This will allow the B instance to have a property that shadows the inherited A
class A {
constructor() {
this.A = 'A';
}
createB() {
let b = new B()
b.A = this.A // set to the instances 'A'
return b
}
}
class B extends A {
constructor() {
super();
this.B = 'B';
}
}
var base = new A();
var derived = new B();
base.A = 'C';
// Create a B instance from A so that
// a new A isn't instantiated and
// fromInstance.A === 'C'
var fromInstance = base.createB();
console.log(fromInstance)
A generic approach could copy all A instance properties to a new B instance. An even more generic approach would be to copy the B object's properties to temporary storage first, and write them back later, so that if A and B had the same property name, the B object's property would take precedence:
class A {
constructor() {
this.A = 'A';
}
createB() {
let b = new B();
let temp = {};
let aProp, bProp;
// save own properties of b
Object.keys( b).forEach( bProp => (temp[bProp]=b[bProp]));
// install own properties of A instance
Object.keys( this).forEach( aProp => ( b[aProp]=this[aProp]));
// write back b properties over A instance properties
Object.keys( temp).forEach( bProp=> (this[bProp]=temp[bProp]));
return b;
}
}
class B extends A {
constructor() {
super();
this.B = 'B';
}
}
var base = new A();
var derived = new B();
base.A = 'C';
// Create a B instance from A so that
// a new A isn't instantiated and
// fromInstance.A === 'C'
var fromInstance = base.createB();
console.log( "derived.A = %s", derived.A);
console.log( "fromInstance.A = %s", fromInstance.A);
Note that in JavaScript terms avoiding Class constructs be syntactically easier because you can change the prototype properties of ordinary functions but not Class constructor functions. However you would probably lose the ability to reliably identify B instances using instanceof B. Using a class expression in CreateB has the same problem - returned objects would not share the same Class constructor.
Just create an instance of B, copy the properties of the current instance of A to it using Object.assign then return it:
createB() {
var b = new B();
Object.assign(b, this);
return b;
}
Example:
class A {
constructor() {
this.A = 'A';
}
createB() {
var b = new B();
Object.assign(b, this);
return b;
}
}
class B extends A {
constructor() {
super();
this.B = 'B';
}
}
var base = new A();
var derived = new B();
base.A = 'C';
var fromInstance = base.createB();
console.log(fromInstance instanceof B);
console.log(fromInstance.A);
So I was thinking if a literal object can inherit properties and methods from a class. Here is the code
var Foo = function(val1, val2) {
this.prop1 = val1;
this.prop2 = val2;
}
var bar = {
//how to inherit properties from Foo class; also add new property
prop3: 'val3'
};
You could achieve this by creating an instance of Foo and then adding properties to that instance like so:
var Foo = function(val1, val2) {
this.prop1 = val1;
this.prop2 = val2;
}
var x = new Foo('valOfProp1', 'valOfProp2');
x.prop3 = 'valOfAddedProp';
Theres no inheritance in your code. Inheritance would look like this:
var Foo = function(val1, val2) {}
Foo.prototype={
prop1:val1,
prop2:val2
};
var bar = {
//how to inherit properties from Foo class; also add new property
prop3: val3
};
And now you could do:
Object.setPrototypeOf(bar, /*to*/ Foo.prototype);
or creating another object:
var instance=new Foo();
Object.assign(instance,bar);
You may do as follows;
var Foo = function(val1, val2) {
this.prop1 = val1;
this.prop2 = val2;
};
Foo.prototype.getProp = function(p){
return this[p]; // NOT!! this.p
};
var bar = {
//how to inherit properties from Foo class; also add new property
prop3: "val3"
};
Object.setPrototypeOf(bar,Foo.prototype);
console.log(bar.getProp("prop1"));
console.log(bar.getProp("prop2"));
console.log(bar.getProp("prop3"));
I am working on a Person constructor function that takes a name and age as its parameters, and trying to implement a method that retrieves all the 'Person' instances current age value and outputs the average. Here's my code...
var Person = (function() {
//private state
var inst = 1;
function Person(name, age) {
this.name = name;
this.age = age;
Object.defineProperty(this, "age", {
get: function() {
return age;
},
set: function(num) {
age = num;
}
});
Object.defineProperty(this, "_id", {
value: inst++
});
}
//Attempt to return number of instances divided by all current Person weights
Person.prototype.aveAge = function() {
return inst;
};
return Person;
}());
var jim = new Person("jim", 32);
var richard = new Person("richard", 27);
richard.age = 28;
var alfie = new Person("alfie", 42);
Person.aveAge() //Returns TypeError: Person.aveAge is not a function
I have set up a variable that is shared across all instances (inst) that increments each time an another instance is created and assigns a unique id. I cannot figure out how I can get to each 'age' value of all Person instances in existence using the aveAge prototype I have added at the bottom. I am also getting a 'TypeError: Person.aveAge is not a function' when I attempt to call it to even test that variable 'inst' holds the correct number of instances. Does anybody know where I am going wrong?
It feels strange to keep ages on a person when it references people. Notice that hanging things on __proto__ makes them available from the constructor (Person), while hanging things on prototype makes them available from the instance (richard). If Age is updated, it needs to be done via setAge so the PeopleTracker knows to update it's memory. Also, in my example, the average is only calculated when needed rather than each time a person wants to know what is is.
var peopleTracker = {
_count: 0,
_ages: [],
averageAge: 0,
addPerson: function (age) {
var pt = peopleTracker;
pt._count += 1;
pt._ages.push(age);
pt.getAverage();
},
getAverage: function () {
var sum = 0,
pt = peopleTracker;
sum = pt._ages.reduce(function (a, b) {
return a + b;
});
pt.averageAge = Math.round(sum / pt._count);
},
update: function (oldAge, newAge) {
var pt = peopleTracker,
ages = pt._ages,
i = ages.indexOf(oldAge);
ages.splice(i, 1, newAge);
pt.getAverage();
}
};
var Person = function (name, age) {
this.name = name;
this.age = age;
peopleTracker.addPerson(age);
};
Person.__proto__ = { // available from the constructor
Constructor: Person,
setAge: function (age) {
var oldAge = this.age;
this.age = age;
peopleTracker.update(oldAge, age);
},
aveAge: function () {
return peopleTracker.averageAge;
}
};
Person.prototype = Person.__proto__; // now also available from the instance
var jim = new Person("Jim", 32),
richard = new Person("Richard", 27),
alfie = new Person("Alfie", 42);
Person.aveAge(); // 34
richard.aveAge(); // 34
richard.setAge(20);
Person.aveAge(); // 31
richard.aveAge(); // 31