Inheritance and polymorphism using arrow functions in JavaScript Classes - javascript

Why do arrow functions take precedence over function declarations in JavaScript Classes?
Example :
class Parent {
work = () => {
console.log('This is work() on the Parent class');
}
}
class Child extends Parent {
work() {
console.log("This is work() on the Child class ");
}
}
const kid = new Child();
kid.work();
The parent work() method fires in this example :
"This is work() on the Parent class"
I just want to understand WHY the arrow function always takes precedence in JS Classes, especially in regards to Inheritance and Polymorphism.

It is nothing to do with being an arrow function. It is taking precedence because it's a class field. Class fields are added as an own property of the instance while methods are added to Child.prototype.work. Even if you change it from an arrow function to a regular function, it will still execute the class field.
When you access kid.work, the order in which the property will be looked is
own properties, directly under kid object (It is found here)
Child.prototype.work
Parent.prototype.work
If still not found, it will be looked inside Object.prototype
class Parent {
// doesn't matter if it an arrow function or not
// prop = <something> is a class field
work = function() {
console.log('This is work() on the Parent class');
}
}
class Child extends Parent {
// this goes on Child.prototype not on the instance of Child
work() {
console.log("This is work() on the Child class ");
}
}
const kid = new Child();
// true
console.log( kid.hasOwnProperty("work") )
// true
console.log( Child.prototype.hasOwnProperty("work") )
// false
// because work inside Parent is added to each instance
console.log( Parent.prototype.hasOwnProperty("work") )
kid.work();
// How to force the Child method
Child.prototype.work.call(kid)

Related

Referring class local variable inside of a method which located in the object that located in the same class

Here is the sample code.
class TestClass {
constructor() {
let question = "How could I refer this variable inside of the nested object?"
}
}
TestClass.prototype.category = {
validate : function () {
return true;
}
, read : function () {
// this in here is not TestClass. How could I refer TestClass Instance(caller) in strict mode ?
console.log('Inside of TestClass.prototype.category.read', this);
if(this.validate) {
console.log('I would like to refer TestClass.question', this.question);
}
}
}
TestClass.prototype.readCategory = function () {
this.category.read();
}
Then I do something like below in chrome editor console.
var test = new TestClass();
test.readCategory();
// result is like below
I would like to refer TestClass.question undefined
As far as I know, I presume like
When using the new keyword, I will generate an instance which contains question variable and the methods which I pushed into prototype
Then it will execute readCategory(), it calls instance.category.read but at this moment, this keyword will point instance.read Object, not the TestClass instance. So this.question will be an undefined value.
So here's the question, how could I able to access the caller(or Class instance) variable?
I found out that when we use class, unable to use this.caller.(automatically applied strict mode).
How would I access the class variable in this situation? Thanx.
You can't, your category variable is completely private to the constructor. Once the constructor returns, it's gone.
Your structure is fairly unusual, but if you really need category to be a subordinate object, but you still want it to have access to TestClass instance it's part of, you might use arrow functions created in the constructor to achieve that, see comments:
class TestClass {
constructor() {
// Make question a property
this.question = "How could I refer this variable inside of the nested object?"
// Make category an instance property using arrow functions
this.category = {
validate : () => {
return true;
}
, read : () => {
console.log('Inside of TestClass.prototype.category.read', this);
if(this.category.validate()) { // <=== Need to add `category.` and `()`
console.log('I would like to refer TestClass.question', this.question);
}
}
};
}
// No reason to define this outside the `class` construct, make it a method
readCategory() {
this.category.read();
}
}
Using the class fields proposal (currently at Stage 3, so you'll need to transpile), you can also write that like this:
class TestClass {
// Make category an instance property using arrow functions
category = {
validate : () => {
return true;
}
, read : () => {
console.log('Inside of TestClass.prototype.category.read', this);
if(this.category.validate()) { // <=== Need to add `category.` and `()`
console.log('I would like to refer TestClass.question', this.question);
}
}
};
constructor() {
// Make question a property
this.question = "How could I refer this variable inside of the nested object?"
}
// No reason to define this outside the `class` construct, make it a method
readCategory() {
this.category.read();
}
}
That's effectively the same thing as the first example; field initializers are executed as though within the constructor.
If you don't want question to be a property on the instance, since those are arrow functions defined in the constructor, you could leave it as a local variable they close over:
class TestClass {
constructor() {
let question = "How could I refer this variable inside of the nested object?"
this.category = {
validate : () => {
return true;
}
, read : () => {
console.log('Inside of TestClass.prototype.category.read', this);
if(this.category.validate()) {
console.log('I would like to refer TestClass.question', question); // <== No `this.`
}
}
};
}
readCategory() {
this.category.read();
}
}

Javascript Child Class method not overriding Parent Class Method

I am trying to override one method from the parent class, but there are some issues.
Below is the code snippet of my scenario which I am trying.
class Parent {
add = () => {
console.log('Parent method');
}
}
class Child extends Parent {
add () {
console.log('Child Method');
}
}
// Creating an instance
const child = new Child();
child.add();
It is calling the Parent method add as that is arrow function, Can someone explain why this is happening. If I make the parent function a simple javascript method then child is able to override.
Additonal Details :
I don't have access to Parent as it is part of library.
I can't make my child class method as instance properties (arrow function)
, the reason for being that there are further
specification written for child (child of child) and If we use arrow
functions we will not be able to call the super.
Child function name can't be renamed.
This is one of few reasons why arrow methods aren't convenient. They limit the ways in which a class can be extended and tested.
Class fields (which arrow methods are) are syntactic sugar for constructor code:
class Parent {
constructor() {
this.add = () => {...};
}
}
Only another arrow method can override parent arrow method, because they are defined in class constructor, not on class prototype:
class Child extends Parent {
add = () => {
/* no super.add here because add is not prototype method */
}
}
If super.add is intended to be used, a workaround is to store parent method:
class Child extends Parent {
superAdd = this.add;
add = () => {
this.superAdd();
}
}
Notice that since this is syntactic sugar for constructor code, the order in which superAdd and add are defined matters.
The parent add is an instance property, and it overshadows the child's class method, which is part of the instance's prototype. It's a bit hacking, but you can rename and delete the class property in the constructor:
class Parent {
add = () => {
console.log('Parent method');
}
}
class Child extends Parent {
constructor() {
super();
this.parentAdd = this.add;
delete this.add;
}
add() {
console.log('Child Method');
this.parentAdd(); // if you need call the parent's method
}
}
const child = new Child();
child.add();

Javascript classes : how to access overridden parent class functions in parent class code

Javascript ES6 ( node 8.4.0 and latest chrome and recent Firefox )
I expected
class Parent {
init(){
console.log("Parent init") ;
this._surname = "McClass" ;
}
constructor() {
console.log("Parent constructor") ;
this.init();
}
get surname(){
return this._surname ;
}
}
class Child extends Parent {
init(){
console.log("Child init") ;
}
constructor() {
super();
console.log("Child constructor") ;
this.init();
}
}
var child = new Child() ;
console.log(child.surname);
to give the following output;
Parent constructor
Parent init
Child constructor
Child init
McClass
(which is what comparable C++ code gives)
Alas, I got this ;
Parent constructor
Child init
Child constructor
Child init
undefined
Am I doing something wrong or is this the correct intended behaviour and if so how is it justified ?
EDIT;
See MinusFour's answer below on how to achieve what I was trying to do / expecting.
As to why the observed output is the "correct" behaviour and justified ;
As Bergi pointed out (in comments) all calls to object methods in js are effectively "virtual" (the last method of that name added to the object's prototype inheritance chain being the first found and hence executed). It turns out calls are still effectively virtual in a class construction context.
C++ does not apply virtual method behaviour during construction but then again Java does and you get the same output (as above) in comparable Java code so there is a precedent for the observed behaviour.
You could do:
Parent.prototype.init.call(this);
class Parent {
init() {
console.log("Parent init");
this._surname = "McClass";
}
constructor() {
console.log("Parent constructor");
Parent.prototype.init.call(this);
}
get surname() {
return this._surname;
}
}
class Child extends Parent {
init() {
console.log("Child init");
}
constructor() {
super();
console.log("Child constructor");
this.init();
}
}
var child = new Child();
To make sure it never gets overridden, but I would suggest you just not override it in the first place.
It is expected behaviour, just because it can be seen in established ES6 class implementations that follow the specs.
this refers to current class instance, which is an instance of Child in the case when Child is instantiated - even in Parent class, because there is only one instance, and it is instanceof Child.
If Child overrides the method, it's its responsibility to provide mechanism to call it. Considering that init follows some documented convention and is the place where class initialization happens in order to make constructor leaner and more testable, it is:
class Parent {
init(){...}
constructor() {
this.init();
}
...
}
...
class Child extends Parent {
init(){
super.init();
...
}
// optional, as long as `init` contains all init logic
constructor() {
super();
}
}
Which results in a sequence:
Parent constructor
Parent init
Child init
Child constructor
If init is supposed to work totally independently in both classes, it shouldn't be overridden. Methods should be named differently, like initParent and initChild. Or any other way to avoid naming collisions can be used, e.g.:
const init = Symbol('Parent init');
class Parent {
[init](){...}
constructor() {
this[init]();
}
...
}
...
const init = Symbol('Child init');
class Child extends Parent {
[init](){...}
constructor() {
this[init](); // totally independent method
}
}

How to call a parent method from child class in javascript?

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'));

Super class methods in javascript

I am writing some objects (classes I guess) in javascript. Class B inherits from Class A. Class A has a method called isValid, and class B overrides that method. I am using the YUI extend function to have Class B extend Class A.
A = function(){
}
A.prototype = {
isValid:function(){
/* Some logic */
return booleanValue;
}
}
B = function(){
}
YAHOO.lang.extend(B, A,{
isValid:function(){
// call class A's valid function
// some more logic for class B.
return booleanValue;
}
});
What I want to be able to do is call Class A's isValid function inside class B's isValid function. The question is, can I access class A's isValid method from class B's isValid method? I know that you can access class A's constructor from inside Class B's constructor with the following line
this.constructor.superclass.constructor.call(this,someParam);
Is something similar possible for methods? If not, what is a good practices for doing this? Currently I am making a helper method called inside the super class' isValid method
A.prototype = {
a_isValid:function(){
// class A's is valid logic
return booelanValue;
},
isValid:function() {return this.a_isValid();}
}
Then I can call the a_isValid function from class B. This does work for me but I would prefer to call the super class' isValid function directly if possible.
From YUI docs:
YAHOO.lang.extend(YAHOO.test.Class2, YAHOO.test.Class1);
YAHOO.test.Class2.prototype.testMethod = function(info) {
// chain the method
YAHOO.test.Class2.superclass.testMethod.call(this, info);
alert("Class2: " + info);
};
Doesn't it work for you? The 4th line should call Class1's (superclass) testMethod.
I am posting another approach for documentation purposes.
If messageFormController derives of formController, you call super.setView as:
messageFormController.setView = function setView(element) {
formController.setView.bind(this)(element);
// Additional stuff
};

Categories

Resources