When using classes in ES6, the 'super' keyword can only be used directly within class methods. Which is perfectly reasonable, but sometimes awkward.
This is the work-around that I've come up with, but is there a better way?
class foo {
constructor () {
}
bar (x) {
console.log('bar x:', x);
}
}
class morefoo extends foo {
constructor () {
super();
}
bar (x) {
let super_bar = super.bar.bind(this);
setTimeout(function () {
//super.bar(x*2); // => 'super' keyword unexpected here
super_bar(x*2);
}, 0);
}
}
let f = new morefoo;
f.bar(33);
Use an arrow function if you want to reference super inside a callback:
class foo {
constructor () {
}
bar (x) {
console.log('bar x:', x);
}
}
class morefoo extends foo {
constructor () {
super();
}
bar (x) {
setTimeout(() => super.bar(x*2), 0);
}
}
let f = new morefoo;
f.bar(33);
Arrow functions treat certain keywords/variables differently (this, arguments, super).
Related
It is easier to show than to describe it. Here is the code
let ns = {};
ns.A = class {
constructor() {
this.Virtual();
}
Virtual() {
}
};
ns.B = class extends ns.A {
constructor() {
super();
alert(this.Field);
}
Field = 0;
Virtual() {
this.Field = 123;
}
}
The alert() says that this.Field equals 0. That is, field initialization in the B class is performed after the A constructor finishes. Is this "by design" in Javascript?
If I put the Field in the prototype of the B class, then everything works OK, just like in any other language. For example
let ns = {};
ns.A = class {
constructor() {
this.Virtual();
}
Virtual() {
}
};
ns.B = class extends ns.A {
constructor() {
super();
alert(this.Field);
}
//Field = 0;
Virtual() {
this.Field = 123;
}
}
ns.B.prototype.Field;
Sorry to bother you here with this, but I don't know where is the right place to report this issue.
From https://github.com/tc39/proposal-class-fields#execution-of-initializer-expressions:
When field initializers are evaluated...
Base class: At the beginning of the constructor execution...
Derived class: Right after super() returns...
Illustration:
class A {
constructor() {
console.log('A constructor start');
this.Virtual();
console.log('A constructor end');
}
Field = (() => { console.log('A field init'); return 1})()
Virtual() {
}
};
class B extends A {
constructor() {
console.log('B constructor start')
super();
console.log('B constructor end')
}
Field = (() => { console.log('B field init'); return 2})()
Virtual() {
console.log('B virtual')
this.Field = 123;
}
};
console.log(new B())
That is, in your code, Field = 0 happens after this.Field = 123, thus overwriting it. The order of declarations doesn't matter.
If you have issues with this behaviour and wish to discuss it, https://github.com/tc39/proposal-class-fields/issues would be the right place.
This question already has an answer here:
Accessing a class field on a superclass
(1 answer)
Closed 3 years ago.
It seems you cannot call a superclass arrow function using super.methodName() within a subclass:
class Parent {
constructor() {
console.log('in `Parent` constructor')
}
parentArrow = () => {
console.log('parentArrowFn')
}
parentMethod() {
console.log('parentMethod')
}
}
class Child extends Parent {
constructor() {
super()
console.log('in `Child` constructor')
}
childMethod() {
console.log('childMethod')
super.parentMethod() // works
super.parentArrow() // Error
}
}
(new Child()).childMethod();
Generates the error:
Uncaught TypeError: (intermediate value).parentArrow is not a function
at Child.childMethod (<anonymous>:21:15)
at <anonymous>:1:7
Is there any way to prevent this so that I can use arrow functions in my parent class, yet ensure they are accessible via super in subclasses?
Public fields or "class instance fields" in es6 classes are not yet supported natively in all environments and even if it is you need to call it through a this reference instead of super as it becomes an instance property rather than a property on the prototype:
class Parent {
constructor() {
console.log('in `Parent` constructor')
}
parentArrow = () => {
console.log('parentArrowFn')
}
parentMethod() {
console.log('parentMethod')
}
}
class Child extends Parent {
constructor() {
super()
console.log('in `Child` constructor')
}
childMethod() {
console.log('childMethod')
super.parentMethod() // works
this.parentArrow() // Calls parentArrow
}
}
new Child().childMethod();
The need to call the arrow function using this instead of super is because when using an arrow function as a method the parentArrow is added as a property of the instance rather than of the prototype and super is used for calling methods declared on the prototype.
Your code can be translated to the code below, when you declare an arrow function inside a class:
constructor() {
console.log('in `Parent` constructor');
// becomes an instance property
this.parentArrow = () => { <----
console.log('parentArrowFn') |
} |
} |
parentArrow = () => { ----
console.log('parentArrowFn')
}
Good day,
I dont know if am can explain this well for you to help but i will like to use a an ES6 class to create an object that can be called like this.
var = varaibles
obj = objects
obj.var
obj.var.method
obj.var.var.method
obj.method.var
and so on.
I can only do one step
obj.var && obj.method
i will kind appreciate if one can help me here thanks
this is what i have done
class Table extends someClass {
constructor() {
super();
this.column = {
sort: () => {
console.log("firing");
},
resize: () => {
console.log("firing");
}
};
this.cells = {
edit: () => {
console.log("firing");
}
};
}
myMethods() {
//BLAH
}
}
From what I understood, here is my solution.
If I return a object full of methods, I can use that object as I like.
class someClass {
// this is a parent method
Parent() {
console.log(`From a Parent`)
}
// a getter that returns an object
get parentWithChild() {
return {
child() {
console.log(`From a Child`)
}
}
}
// a function that returns an object
Methods() {
return {
child() {
console.log(`From a Child`)
}
}
}
}
const cool = new someClass();
cool.Parent(); // From a Parent
cool.parentWithChild.child(); // From a Child
cool.Methods().child(); // From a Child
You can use similar pattern on the extended class too.
I want to keep this in class methods.
I can use arrow functions, but I want to override some methods in extended class.
Now I have this solution and it works:
class Foo {
bar = "Context preserved.";
constructor() {
this.foo = this.foo.bind(this);
}
foo() {
alert(this.bar);
}
}
class Foo2 extends Foo {
foo() {
alert(this.bar + " Class extended");
}
}
class Bar {
bar = "Context lost.";
}
let foo = new Foo2();
let bar = new Bar();
foo.foo.apply(bar); // Context preserved. Class extended
Is it a good practice to do it such way? If it is, is there some keyword in typescript to do it automatically?
like
class Foo() {
public conserved foo() { }
}
which generates:
var Foo = (function () {
function Foo() {
this.foo = this.foo.bind(this);
}
Foo.prototype.foo = function () { };
return Foo;
}());
It's a valid practice and it's being used.
I'm unaware of a way to tell typescript to do this automatically, but you can search the issues for something like it.
You can have a decorator that does that for you, for example:
function construct(constructor: Function, methods: string[], args: any[]) {
var c: any = function () {
return constructor.apply(this, args);
}
c.prototype = constructor.prototype;
let instance = new c();
methods.forEach(name => {
instance[name] = instance[name].bind(instance);
});
return instance;
}
function BindMethods(constructor: Function) {
const methods = [] as string[];
Object.keys(constructor.prototype).forEach(name => {
if (typeof constructor.prototype[name] === "function") {
methods.push(name);
}
});
return (...args: any[]) => {
return construct(constructor, methods, args);
};
}
#BindMethods
class Foo {
bar = "Context preserved.";
foo() {
console.log(this.bar);
}
}
let foo = new Foo();
setTimeout(foo.foo, 10);
(code in playground)
I tested it with this simple use case and it worked just fine.
I have an interface/type defined and I don't want to redefine it everywhere I want to use the mixin. Think facebook's mixins: [] property
Example usecase:
const mixin = root => Object.assign(root, {bar: () => 'baz'));
class Foo {
constructor () { mixin(this); }
test () { console.log(this.bar()) } // <-- this gives an error
}
From what I could find online, this is what i've tried:
interface MyMyxin {
bar () : string;
}
function mixin <T:Object> (root: T) : T & MyMyxin {
return Object.assign(root, {bar: () => 'baz'});
}
class Foo {
constructor () {
mixin(this);
}
test () {
console.log(this.bar()); // <-- no luck
}
}
mixin(Foo.prototype) // <--- this also does not work
But I can't get flow to understand that the mixin method adds an extra property to the object