Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

access decorated class from itself #79

Open
mixtur opened this issue Oct 7, 2016 · 4 comments
Open

access decorated class from itself #79

mixtur opened this issue Oct 7, 2016 · 4 comments

Comments

@mixtur
Copy link

mixtur commented Oct 7, 2016

When you have decorated class, from inside that class should you be able to access decorated or original version of it?

For example.

const decorator = (x) => {
    console.log('test');
    return x;
};

@decorator
class A {
    foo() {
        return new A();
    }
}

const a = new A();//this invokes console.log
const b = a.foo();// maybe this should too?
@otakustay
Copy link

const a = new A();//this invokes console.log

I think it should not invoke console.log, decorator works in design time so console.log is invoked when you define class A but not when you intantiate it

let decorator = x => {
  console.log(1);
  return x;
};

@decorator
class A {

}

new A();
new A();

This code only log once.

For the new A() statement in foo method, it depends on how decorator is implemented:

let decorator = x => {
    x.prototype.bar = () => {
        console.log('bar');
    };
    return x;
};

@decorator
class A {
    foo() {
        let a = new A();
        a.bar();
    }
}

let a = new A();
a.foo();

This code successfully prints bar because decorator modifies the prototype of class A and add a method bar, so new A() just contains a bar method.

let decorator = x => class extends x {
    bar() {
        console.log('bar');
    }
};

@decorator
class A {
    foo() {
        let a = new A();
        a.bar();
    }
}

let a = new A();
a.bar();
a.foo();

However this should throw a TypeError when a.foo() is called because decorator returns a subclass of A so new A() does not contains method bar

To reference the actual decorated subclass in method foo you could change new A() to new this.constructor(), it always works

@mixtur
Copy link
Author

mixtur commented Oct 7, 2016

I see why my example is not actualy showing the problem. Your last is better
And you provided some way to access decorated class.
I figured one can also apply decorator manually and have access to both versions.

const A = decorator(class B {
 a() { return new A(); }
 b() { return new B(); }
})

But still.
Does anyone ever need to access original class from itself when it is decorated using @decorator syntax?
If not, I think the opposite behaviour is better.

@otakustay
Copy link

It's pretty interesting why I believe the new A() state in foo method points the original class A but not the decorated one, in fact outside the class body the reference to A is the decorated version of class:

let decorator = x => class extends x {
    bar() {
        console.log('bar');
    }
};

@decorator
class A {
    foo() {
        console.log(A); // outputs [Function: A]
        let a = new A();
        a.bar();
    }
}

console.log(A); // outputs [Function: _class] but not [Function: A]

I think it is because:

  1. The language evaluation order requires this behavior (class body is evaluated before decorator rolls in), so it's really hard to have A inside class body be the decorated version
  2. It is much the same of named function expression so it is quite intuitive to me

For language itself, I think there should be cases where we need both the original version and decorated version of class, a good example is C# which has both virtual and non-virtual methods along with override and new keyword for methods, in javascript world, A.prototype.bar.call(this) is the non-virtual one and this.bar() is the virtual one

@otakustay
Copy link

otakustay commented Oct 7, 2016

To addition, there is a more tricky way to access both original and decorated version of class:

let B = @decorator class A {
    foo() {
        console.log(A); // Original one
        console.log(B); // Decorated one
    }
};

let a = new B();
a.foo();

It's the same as apply decorator manually

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants