I'm trying to apply this to the constructor of an es6 class to get some kind of "constructor merging" like this:
class D {
constructor(name){
this.name=name
this.methodD=function(){}
}
}
class C extends D {
constructor(name,name2){
super(name)
this.name2=name2
}
}
function Custom(name,name2){
if (this instanceof Custom){
Function.prototype.bind.call(C.prototype.constructor,this,...arguments)
}
}
Custom.prototype.method=function(){}
const cc=new Custom('name','name2')
I'm expecting cc to be constructed using the same constructor as C, so getting cc.name='name';cc.methodD();
Thank you in advance for your help.
You could try to create new instance of C inside of Custom and redefine prototype, using es6 Object.setPrototypeOf().
Please note that in this case Custom will only return object which will be constructed like C and its prototype will not contain methods of C.prototype and D.prototype, only their own methods, like methodD of D class.
Also note, that according to MDN article, this method could affect performance of browser.
class D {
constructor(name){
this.name=name
this.methodD=function(){}
}
}
class C extends D {
constructor(name,name2){
super(name)
this.name2=name2
}
}
function Custom(name,name2){
let cls = new C(...arguments);
Object.setPrototypeOf(cls, Custom.prototype);
return cls;
}
Custom.prototype.method=function(){}
const cc=new Custom('name','name2')
console.log(cc.name)
console.log(cc.name2)
console.log(cc.methodD)
console.log(cc instanceof Custom)
Related
In python there's something like __call__ for this. Consider the following example:
class MyClass {
__call__() { return 'called!' }
}
const myType = new MyClass();
myType(); // called!
The question is what should I replace __call__ with?
I was doing some research, and most of the answers recommend __proto__, but it doesn't seem to work.
It is not possible out-of-the-box, but you can extend Function, and use the Function constructor to forward a call to __call__. If you have multiple classes that need this feature, extend Function only once into -- let's say -- a Callable class, and then inherit your other classes from that:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
__call__() { return 'called!' }
}
let inst = new Class();
console.log(inst());
Background
In JavaScript an object is callable when, and only if, it has the [[Call]] internal slot. But there is (currently) no way to give any given object this slot via JavaScript code. One must start with a function object and extend that.
Adding a constructor, inheritance
The above solution allows the constructor to define properties in the usual way: the constructed object is an instance of the class:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
constructor(data) {
super();
this.x = data;
}
__call__() { return 'called!' }
}
let inst = new Class(42);
console.log(inst instanceof Class); // true
console.log(inst.x); // 42
console.log(inst());
You can use constructor.
class Example {
constructor() {
// gets called on class initialization
}
}
Inside the constructor you can also call other methods if you want.
However this won't create an invoke function like using PHP's __invoke if that's what you meant. If that's what you're looking for then I don't know.
I have a class A that is a subset of class B. It shares many of the properties and methods of class B.
Class A lacks implementation though. So I want all all functionality in class B to to go into class A.
ClassA.prototype = ClassB.prototype;
or
ClassA.prototype += ClassB.prototype
But it seems I have to:
ClassA.prototype.methodA = ClassB.prototype.methodA
ClassA.prototype.methodB = ClassB.prototype.methodB
ClassA.prototype.methodC = ClassB.prototype.methodC
ClassA.prototype.methodD = ClassB.prototype.methodD
for every single method and property. Is there no way I can put implementations in B into A at once?
It's true you can't overwrite the prototype property of functions created via class syntax, because it's both read-only and non-configurable. You can do it if you use function syntax instead as Fullstack Guy points out.
But you probably want to make ClassA extend ClassB:
class ClassA extends ClassB {
// ...
}
Live Example:
class ClassB {
methodA() {
console.log("methodA");
}
methodB() {
console.log("methodB");
}
methodC() {
console.log("methodC");
}
methodD() {
console.log("methodD");
}
}
class ClassA extends ClassB {
// ...
}
new ClassA().methodA();
If not, though, you can copy all the methods using a loop:
for (const name of Object.getOwnPropertyNames(ClassB.prototype)) {
const method = ClassB.prototype[name];
if (typeof method === "function") {
ClassA.prototype[name] = ClassB.prototype[name];
}
}
Live Example:
class ClassB {
methodA() {
console.log("methodA");
}
methodB() {
console.log("methodB");
}
methodC() {
console.log("methodC");
}
methodD() {
console.log("methodD");
}
}
class ClassA {
// ...
}
for (const name of Object.getOwnPropertyNames(ClassB.prototype)) {
const method = ClassB.prototype[name];
if (typeof method === "function") {
ClassA.prototype[name] = ClassB.prototype[name];
}
}
new ClassA().methodA();
But note that if ClassB is a subclass, super within the methods will continue to access ClassB's superclass methods, it won't either be invalid or access ClassA's superclass methods.
You can use Object.create to make a prototype of ClassA inherit from the prototyoe of ClassB:
function ClassB(){
}
ClassB.prototype.methodA = function(){
console.log("methodA");
}
function ClassA(){
//no implementation
}
//Make the prototype of Class A inherit from the ptottype of Class B
ClassA.prototype = Object.create(ClassB.prototype);
const classA = new ClassA();
classA.methodA();
The above is for function constructors, if you want to use ES6 classes, then you simply need to extend the ClassB:
class ClassB{
methodA(){ console.log("methodA"); }
}
class ClassA extends ClassB{
}
const classA = new ClassA();
classA.methodA();
// When you extend another class, the instance methods of super class are inherited
// in the prototype property of the child class
ClassA.prototype.methodA();
As #T.J. Crowder rightfully said the prototype property of the class Object is not configurable and as result you cannot assign another object to it. Also you cannot change the configurable to true once it has been set to false. The only option is to copy the member functions in a loop.
You can verify this through the Object.getOwnPropertyDescriptor() method:
class ClassA{
}
//configurable and writable is false
console.log(Object.getOwnPropertyDescriptor(ClassA, "prototype"));
I try to extend a class with another, who has its constructor overrode, but when i instance this class, it doesn't have its own methods, but has its own properties.
Here's an example which doesn't work properly:
class A {
constructor () {
return {
pi: 3.14
}
}
}
class B extends A {
constructor () {
super();
this.c = 10;
}
d () {}
}
let b = new B();
console.log(b);
Here, b is :
Object {
c:10,
pi:3.14
}
So why the 'd' method is missing ?
EDIT:
Here is a concrete case:
I need to extend a class with HTMLElement, which i can instance and use like html element without registering with document.registerElement.
My code is:
class Element{
constructor(){
return document.createElement('div');
}
}
class Editor extends Element{
constructor(){
super();
}
}
and i want to use my class like this:
let editor = new Editor();
document.querySelector('body').appendChild(editor);
ECMAScript6 class methods are methods of an object's prototype object. You will find the method not in the object itself, but in obj.__proto__.
https://reinteractive.com/posts/235-es6-classes-and-javascript-prototypes
Here you can see and maybe understand what is going on internally with prototyped objects. And yes, the ES6 syntax is just a syntax change, not a new technology.
In an ES6 class with some instance variables and methods, how can you add a mixin to it? I've given an example below, though I don't know if the syntax for the mixin object is correct.
class Test {
constructor() {
this.var1 = 'var1'
}
method1() {
console.log(this.var1)
}
test() {
this.method2()
}
}
var mixin = {
var2: 'var2',
method2: {
console.log(this.var2)
}
}
If I run (new Test()).test(), it will fail because there's no method2 on the class, as it's in the mixin, that's why I need to add the mixin variables and methods to the class.
I see there's a lodash mixin function https://lodash.com/docs/4.17.4#mixin, but I don't know how I could use it with ES6 classes. I'm fine with using lodash for the solution, or even plain JS with no libraries to provide the mixin functionality.
Javascript's object/property system is much more dynamic than most languages, so it's very easy to add functionality to an object. As functions are first-class objects, they can be added to an object in exactly the same way. Object.assign is the way to add the properties of one object to another object. (Its behaviour is in many ways comparable to _.mixin.)
Classes in Javascript are only syntactic sugar that makes adding a constructor/prototype pair easy and clear. The functionality hasn't changed from pre-ES6 code.
You can add the property to the prototype:
Object.assign(Test.prototype, mixin);
You could add it in the constructor to every object created:
constructor() {
this.var1 = 'var1';
Object.assign(this, mixin);
}
You could add it in the constructor based on a condition:
constructor() {
this.var1 = 'var1';
if (someCondition) {
Object.assign(this, mixin);
}
}
Or you could assign it to an object after it is created:
let test = new Test();
Object.assign(test, mixin);
In es6 you can do this without assigning and you can even invoke the mixin constructor at the correct time!
http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/#bettermixinsthroughclassexpressions
This pattern uses class expressions to create a new base class for every mixin.
let MyMixin = (superclass) => class extends superclass {
foo() {
console.log('foo from MyMixin');
}
};
class MyClass extends MyMixin(MyBaseClass) {
/* ... */
}
You should probably look at Object.assign(). Gotta look something like this:
Object.assign(Test.prototype, mixin);
This will make sure all methods and properties from mixin will be copied into Test constructor's prototype object.
I'm surprised to find that none of the answers mentions what I would consider a mixin in the sense of composition (and in contrast to inheritance), which to me is a function that adds functionality to an object. Here's an example making use of both inheritance and composition:
class Pet { constructor(name) { this.name = name } }
class Cat extends Pet { expression = 'miaow' }
class Dog extends Pet { expression = 'bark' }
class Human { constructor(name, age) { this.name = name; this.age = age; } }
class American extends Human { expression = 'say howdy' }
function canSayHello(...contexts) {
for (const context of contexts) {
context.sayHello = function() {
console.log(`Hello my name is ${this.name} and I ${this.expression}`)
}
}
}
canSayHello(Pet.prototype, Human.prototype); // apply the mixin
const garfield = new Cat('garfield');
const pluto = new Dog('pluto');
const joebiden = new American('Joe Biden', 79);
garfield.sayHello();
pluto.sayHello();
joebiden.sayHello();
If I define two classes as follows...
class A {
getParentInstance(...args) {
return new super.constructor(...args);
}
}
class B extends A {}
console.log((new B).getParentInstance().constructor.name);
Object is logged to the console, instead of my desired A. This is due to the super in A.prototype.getParentInstance referencing the superclass of A specifically which is Object. This is opposed to the alternative, which would be super being relative to the current level in the prototype chain -- for B that would be A.
My question is: Is there a way to define methods in such a way to use the relative super at each level of the prototype chain when inherited? Effectively resulting in...
(new B).getParentInstance().constructor.name === 'A'
You could try something like this
class A {
static getParentConstructor() {
return Object;
}
}
class B extends A {
static getParentConstructor() {
return A;
}
}
var b = new B();
var a = new (b.constructor.getParentConstructor())();
A little Object.getPrototypeOf() magic seems to be the trick:
class A {
getParentInstance(...args) {
const thisProto = this.constructor.prototype;
const RelativeSuperConstructor = Object.getPrototypeOf(thisProto).constructor;
return new RelativeSuperConstructor(...args);
}
}
class B extends A {}
console.log((new B).getParentInstance().constructor.name);
results in the correct "super" being grabbed, and thus logs 'A' as desired.