Javascript functions, multiple prototype inheritance - javascript

I have an assignment to implement a diagram using javascript prototypes and constructors.
For now I have to implement multiple inheritance using prototypes. I know how to implement single inheritance and I am stuck on inheriting multiple prototypes.
This question is focusing on WeatherData inheriting Event and DataType objects.
import { DataType, Event } from "./../common/EventData.mjs"
export function WeatherData(value, { time, place }, { type, unit, isInternational }) {
Event.call(time, place)
DataType.call(type, unit, isInternational)
this.value = value
}
WeatherData.setPrototypeOf(WeatherData.prototype, Event.prototype)
WeatherData.setPrototypeOf(WeatherData.prototype, DataType.prototype)
WeatherData.prototype.getValue = function () { return this.value }
I havent tested the code but I am sure it's wrong because the second .setPrototypeOf() overwrites the first function, which means that the WeatherData's prototype will be DataType.
I have searched the internet and could not find answer for this, maybe because this methodology is obsolete.

One could give the OP's code a refactoring try of muti-inheritance glue-code like this ...
import { DataType, Event } from "./../common/EventData.mjs"
function WeatherData(
value,
{ time, place },
{ type, unit, isInternational }
) {
// - close to an instance level super call but done twice.
//
// - technically applying two function based mixins.
Event.call(this, time, place);
DataType.call(this, type, unit, isInternational)
this.value = value
}
// - prototype level kind of multiple superclass extension.
//
// - technically mixed-in prototype objects via
// `Object.assign`
WeatherData.prototype = Object.assign(
// ... aggregate a compound prototype.
{},
Event.prototype,
DataType.prototype,
);
// prevent latest mixed-in super-constructor, here
// `DataType`, from being the sub-classed constructor.
WeatherData.prototype.constructor = WeatherData;
WeatherData.prototype.getValue = function () {
return this.value;
}
export/* default*/ WeatherData;
The above constructor implementation covers the mixin part at instance/object level. The code which aggregates and assigns a prototype compound from two other prototype objects is the closest one can come to multiple inheritance with what is available in JS.
But the above code's design also is flawed in a way that such a compound prototype does loose any further linkage into any of the possibly available prototype chains of either Event or DataType.
Thus from a modeling perspective it was better if the available code base was provided in a way that one could let WeatherData inherit from DataType whereas a prototype agnostic implementation of Event could be applied additionally as function based mixin.

Related

On a memory level, is there a difference between creating an object and creating a class instance in JS?

Let's say I want to create an array of Person using a random data library.
I could do something like
import {generateRandom} from 'someLib'
let people = []
function getPerson() ({
name: generateRandom.string()
age: generateRandom.number()
})
for (let i = 0; i < 10; i++) {
people.push(getPerson())
}
But I could also do something like
import {generateRandom} from 'someLib'
class Person {
constructor() {
this.name = generateRandom.string(),
this.age = generateRandom.number()
}
}
let people = []
for (let i = 0; i < 10; i++) {
people.push(new Person())
}
On a memory level, is there any difference in the outcome?
(This is just a theoretical question, I am not trying to solve any problem in particular)
I have found this question that is related to this Difference between creating a class in javascript to create an object and creating an class and object in Java
Which states that there are no classes in JS.
Is this just syntactic sugar? 2 ways of doing exactly the same thing?
If you want to examine the memory yourself, you can put this code in the console:
class Test{};
const test = new Test();
class Class {};
test.Class = Class;
test.classObj = new Class();
function func() {return {};};
test.func = func;
test.funcObj = func();
Take a heap snapshot with google chrome dev tools, sort by constructor and find the Test class.
You can then examine the memory of these functions and objects. Here's a screenshot of what I got:
You can see that the object instantiated by the class constructor is slightly larger than the one instantiated by the function. Expanding the prototype, you can see they both use the Object constructor, but the classObj has the additional Class constructor in its prototype chain.
You can also see that the class constructor appears to retain more memory than regular functions, retained size meaning memory that will be cleaned up by garbage collection if the function is no longer in use.
Seems to be an extra 172 bytes for the class constructor, and 24 bytes per object for an empty object.
Following VLAZ's comment, here's the results with 10 methods added, and 10 instances.
class Test{};
const test = new Test();
class Class {
method0(){};
method1(){};
method2(){};
method3(){};
method4(){};
method5(){};
method6(){};
method7(){};
method8(){};
method9(){};
};
test.Class = Class;
for (let i=0; i < 10; i++){
test["classObj" + i] = new Class();
}
function func0(){};
function func1(){};
function func2(){};
function func3(){};
function func4(){};
function func5(){};
function func6(){};
function func7(){};
function func8(){};
function func9(){};
function constructorFunc() {
return {
method0: func0,
method1: func1,
method2: func2,
method3: func3,
method4: func4,
method5: func5,
method6: func6,
method7: func7,
method8: func8,
method9: func9,
};
};
test.constructorFunc = constructorFunc;
for (let i=0; i < 10; i++){
test["funcObj" + i] = constructorFunc();
}
The shallow size of the class objects are now much smaller. This seems to be due to the fact that they can just store a reference to the class prototype rather than reference all of their methods directly.
At first glance, the retained size of Class seems to be smaller than constructorFunc, but expanding Class you can see a property named prototype which is an object retaining an extra 1.38 KB. Adding that to the 520 B of the class itself pushes it above the retained memory of constructorFunc. But the memory saved by creating instances of the class instead of an object will outweigh that pretty quick.
So seems like classes are the way to go.
Your first getPerson() is invalid syntax and the function does not even attempt to return anything (if it was valid syntax). So, people.push(getPerson()) would generate an array of undefined(if the syntaxError was fixed) which will be entirely different than your second code block which generates an array of objects.
If, what you meant to ask about was something like this:
let people = []
function getPerson() {
return {
name: generateRandom.string()
age: generateRandom.number()
}
}
for (let i = 0; i < 10; i++) {
people.push(getPerson())
}
Then, this and your class will each create an array of objects and those objects will each have your two properties on them. The Class will contain some additional setup on the prototype that allows for subclassing, but if you're just asking about using the object with two properties, then these two would fundamentally do the same thing.
On a memory level, is there any difference in the outcome?
Without methods, your two properties will occupy the same amount of memory. The two properties are assigned directly to the object so those two properties use the same memory.
The class will actually set up a few other things on the object like a .constructor property and the object will get its own separate prototype. The prototype is shared among all instances. So, there could be slightly more memory usage for the class instance, but it's unlikely this makes a material difference.
If you defined methods directly on the object in the first implementation and methods in the class in the second implementation, then the class would definitely be more efficient because there would be one copy of the methods on the prototype vs. many separate copies of the methods on each instance of the object. But, you didn't show that in your question.
Which states that there are no classes in JS. Is this just syntactic sugar? 2 ways of doing exactly the same thing?
Classes are syntactic sugar, but they make it easy to define things in an efficient way without having to do a bunch of manual things. And, because the language sets things up for you, then everyone using a class definition creates code that works the same way.
You can manually build the same object that instantiating an instance of a class does, but it's a lot more code to do write all the bookkeeping that a class does for you. Your simple object with just two instance properties doesn't show any of that or need any of that, but when you start having methods and sub-classing things and overriding base methods and then calling base methods in your derived implementation, the class definition takes care of a some bookkeeping for you and simply lets you write, good extensible code faster and with everyone doing it the same way. Again, your simple object with just two properties does not show or need what a class does, but other uses of objects do.
For example, the class definition and new Person() creates an object that automatically sets yourObject.constructor to be the constructor that created the object. This allows other code to know what type of object it is or allows other code to abstractly create new instances of the same object. Methods are put on the prototype of the object and made non-enumerable. Constructors can call super(...) to execute the base class constructor (whatever it happens to be). Without the class definition, you have to do that very manually by calling the exact right function in the base class.

Composition and Mixins with JavaScript

I am learning about compositions in Javascript. So I want to ask if this is correct way of doing things.
I made some exercises that look like this:
class Animal {
// constructor() {
// }
eat = () => {
console.log("this creature is eating");
}
}
const AnimalsWithWings = superclass => class extends superclass {
constructor(Feathers, ...args) {
super(...args);
Object.assign(this, { Feathers });
}
}
const CanDive = superclass => class extends superclass {
// constructor( ...args) {
// super(...args);
// }
dive = () => {
console.log("Can Dive");
}
}
class Duck extends AnimalsWithWings(CanDive(Animal)) {
constructor(eats, ...args) {
super(...args);
Object.assign(this, { eats });
}
}
const duffy = new Duck("watermelon", true);
console.log(duffy);
duffy.dive();
duffy.eat()
I am still in learning process so I just need some pointers.
Did it do more or less what you expected? Then, sure, it's a correct way to do it, whatever "correct" means here.
It looks to me like it does what it was meant to do when I pop it into the console. I can't really say much more on your code specifically because I'm not sure what concrete domain it's trying to model, aside from maybe breaking down Ducks into atomic pieces.
If you're going to do it this way, though, I'd personally prefer to use a params object instead of just changing the constructor signature like that with AnimalsWithWings. That way, the order of extra parametrizations doesn't depend on the order in which the mixins were applied, which I would consider a Surprise. Surprises are bad.
const AnimalsWithWings = superclass => class extends superclass {
// Everyone receives the same `params` object.
// They only take what they know about, and ignore the rest.
constructor(params) {
super(params);
Object.assign(this, { Feathers: params.Feathers });
}
}
Even more personal opiniony, I'd name them WithDiving and WithWings instead, just to keep a somewhat consistent naming scheme, and to better imply that these are modifiers, not "real" base classes.
Your code does saddle every Duck with a prototype chain 4 prototypes long, but eh, whatever. If it somehow becomes a performance problem then you can create a utility function to optimize the mixin process or something. Flatten the prototypes, maybe.
Your code does also let you call super.method() in methods, though it's debatable whether you should ever use that in a mixin at all. I'd say you shouldn't, unless you want your mixins to implicitly depend on each other, which is a Surprise.
There are plenty of other ways of doing mixins, too.
You could create a utility function to flatten all the prototypes into a single new one and return a base class from that which you extend. (Just be sure to iterate property descriptors rather than just using Object.assign() when doing that flattening, if you want to properly handle things like get/set accessors, etc.)
You could eschew Classes and just directly create prototype objects and use Object.create() to create instances. (same thing about iterating property descriptors.)
You could create a Duck prototype using a bunch of iterative calls to Object.create() instead of iteratively extending base classes.
You could control the additional behaviors with helper Controller Classes instead of composing behavior directly into the base.
You could deal just in plain objects with data, and pass the objects to functions that expect the object to have certain properties on it in order to do things. (Amusingly, called "duck typing") I'll grant that's not really mixins, just calling functions, but if it accomplishes the same thing in effect...
Probably a bunch others I can't really think about at the moment. It's all sticking sets of behaviors onto some base thing.

Javascript - Prototyping on DOM elements

I'm building custom libraries to handle GUI and creating divs and stuff programatically. I also want to extend these objects with children and methods to do something like this:
Function CustomElement() {
this = document.createElement('div');
///--------
Some custom properties
///--------
}
CustomElement.prototype.customMethod = function(args) {
///--------
Some code here
///--------
};
var elem = new CustomElement();
document.body.appendChild(elem);
elem.customMethod(args);
I've thoroughly searched for an answer but found none. How can I accomplish this?
Note: I'm posting from my cell phone. Please excuse me if the code looks awful. I'll correct it as soon as I have access to a PC.
I appears you are confused between classical languages such that you are probably use to, and prototypical like languages such as Javascript.
Also, in your example, assigning the value of this is an invalid statement.
In Javascript, instead of creating children of a super class, we create objects that inherit the properties of other objects through the prototype chain. Still with me? This means that your customMethod is not technically a method, rather it is a property called customMethod which has the value of a function object.
Every constructor object (which is just a fancy name for your CustomElement function) has a magical property named prototype as you have discovered. Objects don't have this property, but they do have an implicit reference to their constructor's prototype object. This means you can call your customMethod as if it were a property of elem, but it is really a property of the constructors prototype object. So I guess you could say the prototype object is kind of like a parent and the object is kind of like a child (although this is incorrect terminology). This prototype object may also again have an implicit reference to it's constructor prototype, which may reference it's constructor prototype... and so on. That's why its called the prototype chain.
So to answer your question:
I also want to extend these objects with children and methods... How can I accomplish this?
For a suggestion to emulate child like inheritance, see below. However, your library requires a different approach...
A common angle of attack is to create a constructor which creates a new object, with a new Element object as a property of that object. For example:
function CustomElement(doesLikeTrains) {
// keep element in this property
this.nativeElement = document.createElement('div');
// other properties are separate
this.likesTrains = doesLikeTrains;
}
// these are also separate
CustomElement.prototype.doesLikeTrains = function() {
return this.likesTrains;
};
// Lets make objects!
var elem1 = new CustomElement(true);
var elem2 = new CustomElement(false);
// use object property and inherited properties
// we can still use the element ok
document.body.appendChild(elem2.nativeElement);
elem1.doesLikeTrains(); // prints true
elem2.doesLikeTrains(); // prints false :(
The DOM element assigned to the nativeElement property. This means you may add other properties without changing the native element object, but still have access to them. Both elem1 and elem2 inherit the same doesLikeTrains property with the same value, but each have their own likesTrains property, which is initialised in the constructor, and can keep a value specific to the object instance.
The advantage of this is that you could change the doesLikeTrains function to always return true, and because all objects created using your CustomELement constructor inherit the same prototype, all objects would then like trains regardless!
How would one create children like objects?
To emulate a child structure, consider...
function CustomOtherElement(likesTrains, runsOnCoal) {
// create new object and inherit from CustomElement
function EmptyConstructor() {}
EmptyConstructor.prototype = new CustomElement(likesTrains);
// add extra stuff to CustomOtherElements only
EmptyConstructor.runsOnCoal = runsOnCoal;
EmptyConstructor.isTrainSuperFan = function () {
return "Hoot hoot, chugga chugga!";
}
// return the new object
return new EmptyConstructor();
}
// now you can do
var elem3 = CustomOtherElement(true, true);
document.body.appendChild(elem3.nativeElement);
elem3.doesLikeTrains(); // true
elem3.isTrainSuperFan(); // "Hoot hoot, chugga chug!"
The above uses this new CustomOtherElement constructor to make an object that inherits CustomeElement and then add some new properties to this new object. Now you can use both the inherited properties from CustomElement and the new ones created on elem3! Happy Javascripting!
Resource: ECMAScript Language Specifications 5.1 section 4.2.1 (Objects)
Consider the approach sometimes called "parasitical inheritance". In this pattern, you write a constructor function, but return something else after adding methods/properties to it, such as
function CustomElement() {
var elt = document.createElement('div');
///--------
Some custom properties
///--------
elt.customMethod = function(args) {
///--------
Some code here
///--------
};
return elt;
}
var myCustomElement = new CustomElement();
This can be simpler, and more reliable, than trying to subclass HTMLElement, which can be a delicate operation, or wrapping the underlying HTML element, as other answers suggest.
Some might complain that the above approach is fat or slow because the "prototype" methods are being placed on each instance. However, that's something that's not really an issue on modern machines and browsers.
In any case, once we've come this far, we need to ask why we are trying to use constructors and new at all, when we can simply say:
function makeCustomElement() {
var elt = ...;
// set custom properties
// set custom methods
return elt;
}
var myCustomElement = makeCustomElement();
Defining a "subclass" is as simple as:
function makeCustomElementSubclass() {
var elt = makeCustomElement();
// set custom properties and methods
return elt;
}
In none of the cases above are prototypes being used (except methods on the built-in prototype such as HTMLElement). They're not really necessary. As I understand it, this is the direction in which mega-guru Douglas Crockford has gravitated in his style. Many cases where we see people using prototypes, it is a matter of "let me figure out a way to do this using prototypes, because they exist and I sort of think I'm supposed to be using them", or "let me figure out a way to do this using prototypes because they sort of behave like the classes I'm used to from C++/Java/C#", or "let me use prototypes to do this because putting methods once on prototypes is so much more efficient than putting them on each object"--but none of these are compelling reasons.

Like Java, should Object.call be the first call in Javascript constructor too?

I was writing some OOP code in Javascript and the design of the inheritance raised a question. Like Java should super constructor call be the first line in subclass constructor in Javascript?
var Effect = function (container, height, width) {};
var XEffect = function (container, height, width, resource) {
Effect.call(container, height, width ); // really necessary to be first line?
};
To quote Douglas Crockford: "JavaScript has a class-free object system in which objects inherit properties directly from other objects. This is really powerful, but it is unfamiliar to classically trained programmers. If you attempt to apply classical design patterns directly to JavaScript, you will be frustrated. But if you learn to work with JavaScript's prototypal nature, your efforts will be rewarded."
Check out Object.prototype for how inheritance is handled in JavaScript.
In your code snippet, Effect.call(container, height, width); would simply evaluate as undefined unless you specifically define it before hand. To call the functionality of your Effect function, you would simply invoke it as Effect().
It depends on exactly what the Effect constructor does.
The object and its prototype chain are already set up by the time you reach the first line of code in the constructor. That is to say, before XEffect's constructor is set up, this already exists, and its __proto__ is already set to XEffect.prototype. So your object is already an XEffect, which inherits from Effect.
However, by this point, none of the code in Effect or XEffect has been run yet. The XEffect code will be run, since it's part of the constructor function, but Effect won't be run unless you tell JavaScript to do so. If you wish to call Effect explicitly, you can. As you described, Effect.call(this, ...) or Effect.apply(this, []) are probably the ways you'd want to do that.
The catch is that Effect runs when you tell it to run: no later, no sooner. If Effect writes to some properties of the object, and then code later in XEffect writes to those same properties, XEffect wins: those are the values that the object will have when its over. If you reverse this order -write to properties inside XEffect, then call Effect, which writes to those same properties- then Effect wins.
If you're used to writing in a "class-based" style, then calling Effect first is probably closest to what you want. It writes to the object's properties, and then XEffect (potentially) overwrites them. But you don't have to do it that way, if you have some reason that you want Effect to win.
Personally, I don't like the object-oriented systems implemented in languages like Java and C++. My favourite object-oriented system is that implemented by the Fortress programming language. For example, consider the following Fortress code taken from the famous blog post Why Object-Oriented Languages Need Tail Calls written by Guy Steele (a.k.a. The Great Quux):
trait IntSet
getter isEmpty(): Boolean = false
adjoin(x: ZZ): IntSet = AdjoinObject(self, x)
union(other: IntSet): IntSet = UnionObject(self, other)
contains(y: ZZ): Boolean
end
object AdjoinObject(s: IntSet, x: ZZ) extends IntSet
contains(y: ZZ) = (y = x) OR: s.contains(y)
end
object UnionObject(s1: IntSet, s2: IntSet) extends IntSet
isEmpty: Boolean = s1.isEmpty AND s2.isEmpty
contains(y: ZZ) = s1.contains(y) OR: s2.contains(y)
end
object EmptyObject extends IntSet
isEmpty: Boolean = true
contains(y: ZZ) = false
end
Object definitions in Fortress introduce object constructors. These constructors may either be nullary (e.g. EmptyObject) or parameterized (e.g. AdjoinObject and UnionObject).
Traits are similar to abstract base classes. Hence, you can't create an instance of a trait directly. Instead you need to create a concrete object that implements the interface of the trait.
Note that in this scenario you never need to call a superclass constructor because there is no superclass constructor. All the superclasses (traits) are abstract. They have no state and hence they do not need to be initialized. The state of the object is encapsulated within the object constructor. This is important because it promotes data abstraction, an important property of object-oriented programs.
So how would this Fortress code look in JavaScript?
There are many ways to write object-oriented code in JavaScript. However, the following Fortress-style object-oriented JavaScript code is my favorite:
class IntSet {
get isEmpty() { return false; }
adjoin(x) { return AdjoinObject(this, x); }
union(other) { return UnionObject(this, other); }
contains(y) { throw new Error("Abstract method invocation"); }
}
const AdjoinObject = (s, x) => new class extends IntSet {
contains(y) { return y === x || s.contains(y); }
}
const UnionObject = (s1, s2) => new class extends IntSet {
get isEmpty() { return s1.isEmpty && s2.isEmpty; }
contains(y) { return s1.contains(y) || s2.contains(y); }
}
const EmptyObject = new class extends IntSet {
get isEmpty() { return true; }
contains(y) { return false; }
}
const Empty = EmptyObject;
const example = Empty.adjoin(1).adjoin(2).union(Empty.adjoin(3));
console.log(example.contains(1)); // true
console.log(example.contains(2)); // true
console.log(example.contains(3)); // true
console.log(example.contains(4)); // false
Uncomplicated solid object-oriented programming in JavaScript. In addition, you do not need to worry about superclass constructors.

Simple 'static' in Javascript 'base class'

I've read through most of the Javascript inheritance references, but I'm afraid I'm still scratching my head as to how to do this.
I'm trying to put together several classes, in the general sense, that will have similar behavior and thought using prototypes might be a way to achieve that. So I create a base class as follows:
function my_base_class()
{
var a_common_object = undefined; // The true value of this can't be set until runtime.
// some other stuff ...
};
Ideally, I'd like a_common_object to be private or at least protected, but just getting it working would be a good first step. I then need to create several derived classes of which this might be one:
function my_derived_class()
{
this.do_something_to_common_object = function()
{
// Here I need to reference my_base_class.a_common_object but
// at this point there's no relationship between the two classes
};
};
I now set the prototype of my_derived_class while creating an instance:
my_derived_class.prototype = new my_base_class();
var my_derived_class_inst = new my_derived_class();
So at this point I'm hoping that I have an object - my_derived_class_inst which has traits of my_base_class including the static object a_common_object which I can access.
I have two questions:
How do I refer to a_common_object within my_derived_class when
there's no relationship established between the two classes?
How can I then change a_common_object to its true value, so that
all derived classes seamlessly pick up the new value.
Please don't simply refer me to the standard reference web sites on inheritance as I've read most of them through and I'm still no wiser. It seems to me that the answer should be really simple but so far it escapes me. Many thanks.
do_something_to_common_object() really doesn't have a way of reaching a_common_object directly.
a_common_object isn't a member of the instance created for the prototype. It's a local variable scoped inside the constructor. So, only a function that's also defined within the constructor can reach it (ref: closures):
function my_base_class()
{
var a_common_object = undefined;
Object.defineProperty(this, 'a_common_object', {
get: function () {
return a_common_object;
}
});
// ...
}
function my_derived_class()
{
this.do_something_to_common_object = function()
{
console.log(this.a_common_object); // uses getter to retrieve the value
};
};
It would still be publicly accessible, but your options are limited as JavaScript doesn't yet support or have an equivalent to access modifiers.
Though, with Object.defineProperty(), it would at least be read-only so far and non-enumerable by default (won't appear in a for..in loop).
At least until #2, where you'd need to also have a setter. Though, it would be a chance to validate the value being storing it.
Object.defineProperty(this, 'a_common_object', {
// ....
set: function (value) {
if (/* validator */) {
a_common_object = value;
}
}
});

Categories

Resources