In the following example, how can I directly call Base.foo() from Base.bar(), I understand why it's calling Test.foo() first, but is there any way to prevent this.
class Base {
constructor() {
console.log('Base constructor')
}
foo() {
console.log('Base foo')
}
bar() {
console.log('Base bar')
this.foo();
}
}
class Test extends Base {
foo() {
console.log('Test foo');
super.foo();
}
bar() {
console.log('Test bar');
super.bar();
}
}
const test = new Test();
test.bar();
Output would be:
Base constructor
Test bar
Base bar
Test foo
Base foo
My expected output would be:
Base constructor
Test bar
Base bar
Base foo
You can use standard JavaScript features while using ES2015 classes. In this case, simply use Function.prototype.call():
'use strict'
class Base {
constructor() {
console.log('Base constructor')
}
foo() {
console.log('Base foo')
}
bar() {
console.log('Base bar')
Base.prototype.foo.call(this);
}
}
class Test extends Base {
foo() {
console.log('Test foo');
super.foo();
}
bar() {
console.log('Test bar');
super.bar();
}
}
const test = new Test();
test.bar();
Related
I have a class testClass and in testClass there is a getter called testGetter and a public object called testObject. In testObject I have a function nestedFunction which attempts to call testGetter but cannot as the scope for this is in the testObject. How could I call the getter (or any function) from the object?
class testClass {
get testGetter() {
return "test"
}
testObject = {
nestedFunction : function(){
console.log(this)
return this.testGetter
}
}
constructor()
{
console.log(this.testObject.nestedFunction())
}
}
new testClass()
Output:
{ nestedFunction: [Function: nestedFunction] }
undefined
Classic use case of arrow functions.
class testClass {
get testGetter() {
return "test"
}
testObject = {
nestedFunction: () => {
console.log(this)
return this.testGetter
}
}
constructor() {
console.log(this.testObject.nestedFunction())
}
}
new testClass()
I have a really simple JS lib (called trysinon.js) that looks like this:
export function foo() {
bar();
}
export function bar() {
return 2;
}
And I have the following test
import expect from 'expect';
import sinon from 'sinon';
import * as trysinon from 'trysinon';
describe('trying sinon', function() {
beforeEach(function() {
sinon.stub(trysinon, 'bar');
});
afterEach(function() {
trysinon.bar.restore();
});
it('calls bar', function() {
trysinon.foo();
expect(trysinon.bar.called).toBe(true);
});
});
And the test is failing. How can I ensure the test passes?
Because in foo(), you called bar() which is inner function of trysinon.js. This bar() is different with the bar() you stubbed which is exported. The best way is to change trysinon to class, or called exported bar() in foo() as following.
function bar() { return 2; }
module.exports.bar = bar;
function foo() {
module.exports.bar();
}
module.exports.foo = foo;
then you can stub bar() with sinon.stub(trysinon, 'bar').returns(2)
Hope this can help you.
I use arrow function instead, and it works.
export const foo = () => {
bar();
}
export const bar = () => {
return 2;
}
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).
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.
There is a way in JS to execute a default method when I call a instance?
Example: Let's assume I have the following class named MyClass, then I start a instance of this class named foo, I want that when I call foo execute the default method of MyClass.
class MyClass {
constructor () {
this.props = {
question: 'live, universe, everything',
answer: 42
}
}
default () {
return this.props
}
hello () {
return 'hello world'
}
}
const foo = new MyClass()
// execute the default method
console.log(foo) // log: {question: 'live, universe, everything', answer: 42}
// execute hello method
console.log(foo.hello()) // log: hello world
The only default method that is called when instantiating an object is the constructor.
In ES6 you can return whatever you want from the constructor so the following code is valid:
class MyClass {
constructor () {
var instance = {
question: 'live, universe, everything',
answer: 42,
hello: () => { return 'hello world' }
}
return instance;
}
}
You can then instantiate an object like this:
var foo = new MyClass();
foo.hello(); //Hello world
console.log(foo.question); //live, universe, everything
console.log(foo.answer); //42