Difference between proxy in class constructor vs proxy with construct handler - javascript

I'm finding different implementations to hook all instances of a class to a proxy.
Inside the class constructor (example):
class Foo {
constructor(...args) {
//...
return new Proxy(this, {
//...
});
}
}
With the construct handler (example):
class Foo {
constructor(...args) {
//...
}
}
new Proxy(Foo, {
construct(Foo, args, newTarget) {
// ...
return Reflect.construct(Foo, [...args], newTarget);
},
//...
});
Is there a difference in these implementations?
I want to make a generic setter and getter; so in the second alternative I would have to create a proxy inside the proxy, which in terms of memory allocation and performance would be the same I think.
Is it so?

Related

How to compose a constructor in ES6?

I tried to write a mixin function based on the ideas described here for inheritance. Method mixins are pretty easy, but I am struggling with constructor mixins.
I try to write a function, which takes a class and returns a new class, with an extended constructor.
This is my example:
function extend_constructor(original, extension) {
let extended = function (...args) {
console.log("extended constructor")
original.prototype.constructor.apply(this, args)
extension.apply(this, args)
}
Object.defineProperty(extended, 'name', Object.getOwnPropertyDescriptor(original, 'name'))
extended.prototype = Object.create(original.prototype)
Object.defineProperty(extended.prototype, 'constructor', {
value: extended,
writeable: true,
configurable: true})
return extended
}
function extension(id) {
console.log(`Extension: ${id}`)
}
I works with function classes.
function Basefunc(id) {
console.log(`Basefunc: ${id}`)
}
new Basefunc(1)
const BasefuncEx = extend_constructor(Basefunc, extension)
new BasefuncEx(2)
But fails with ES6 classes:
class Baseclass {
constructor(id) {
console.log(`Baseclass: ${id}`)
}
}
new Baseclass(3)
const BaseclassEx = extend_constructor(Baseclass, extension)
new BaseclassEx(4)
The error is:
TypeError: Class constructor Baseclass cannot be invoked without 'new'
But I think using new is not what I want, because new will create an instance of the original class and will apply the original constructor to it. I want to apply the original constructor of the original class to the instance of the extended class. And I am wondering how to do this with new.

Working of a Autobind Decorator in typescript?

Hello I am curious about the working of the decorator in Typescript for binding 'this' to functions in Typescript.
function autoBind(
target:any,
methodName:String,
descriptor:PropertyDescriptor
){
console.log("Calling Decorator");
const originalMethod = descriptor.value;
const adjustableDescriptor: PropertyDescriptor = {
configurable : true,
get(){
console.log("Calling get");
const boundFn = originalMethod.bind(this);
return boundFn;
}
}
return adjustableDescriptor;
}
class ProjectInput {
constructor(){
this.configure();
}
#autoBind
private submitHandler(event: Event){
console.log("Calling submit handler");
event.preventDefault();
console.log("Submitting data ...");
console.log(this.titleInputElement.value);
}
private configure() {
this.element.addEventListener("submit",this.submitHandler);
}
}
const projInput = new ProjectInput();
What I did :
I created a Class ProjectInput and in the constructor i am calling the configure method so that i can add EventListeners and handle user submit data and for binding 'this' so that it reference the right object.
I created a Decorator in typescript that will call automatically as soon as the class declared
Everything is fine but I want to know the behind the scenes of the decorator how it binds the this to the function.
I came here, hoping I'd get a more thorough answer than what I'd be able to find, but at least it encouraged me to dig a little further.
Taken directly from React & Autobinding:
Autobind Decorator is an NPM package which binds methods of a class to the correct instance of this, even when the methods are detached. The package uses #autobind before methods to bind this to the correct reference to the component's context.
import autobind from 'autobind-decorator'
class MyComponent extends React.Component {
constructor() {
/* ... */
}
#autobind
addTask(task) {
/* ... */
this.setState({ task });
}
#autobind
myMethod2(task) {
/* ... */
this._anotherBindedMethod(task);
}
render() {
return (
/* ... */
)
}
}
his seems like a simple solution, but I'd rather not have to add a line above each individual method inside each of my React components. Not to worry, Autobind Decorator is smart enough to let us bind all methods inside a component class at once. Like so:
import autobind from 'autobind-decorator'
#autobind
class MyComponent extends React.Component {
constructor() {
/* ... */
}
addTask(task) {
/* ... */
this.setState({ task });
}
/* ... */
}
And just like that, this issue is resolved.
Anyway, hope that helps. Reading it a couple times, helped me. Cheers.
Be careful cause in your code getter returns a new function each time is called and this can potentially lead to memory leaks. This happen cause .bind returns a new function.
So for example if you do .addEventListener('click', this.submitHandler) you're adding a new function each time. .removeEventListener('click', this.submitHandler) will not remove nothing cause will not match any listener
You can easily test this is truth like this
const projectInput = new ProjectInput();
projectInput.submitHandler === projectInput.submitHandler; // false
So an easy fix to your code could be this one
function autobindFunction(
target:any,
methodName:String,
descriptor:PropertyDescriptor
){
console.log("Calling Decorator");
if(typeof descriptor.value !== 'function') {throw new TypeError("cannot decorate prop that is not a function")}
const bound = descriptor.value
const adjustableDescriptor: PropertyDescriptor = {
configurable : true,
value: function (...args: any[]) {
return bound.apply(this, args)
}
}
return adjustableDescriptor;
}
class Test {
#autobindFunction
hi() {
console.log("asd")
}
}
const a = new Test()
console.log(a.hi === a.hi) // true
In this way the reference of the function is stable and the function will be always the same

Class composition in TypeScript

I want to build a class that can compose multiple objects and use any of their interfaces.
Class A can use any of the interfaces of Class B and C
B can use any of the interfaces of C
C can use any of the interfaces of B
I have the above functionality written in JavaScript and I was wondering what's the best and correct way to achieve the same using TypeScript:
import { findLast, isFunction } from "lodash";
class Composite {
constructor(behavior) {
this.behaviors = [];
if (behavior) {
this.add(behavior);
}
}
add(behavior) {
behavior.setClient(this);
this.behaviors.push(behavior);
return this;
}
getMethod(method) {
const b = findLast(this.behaviors, (behavior) =>
isFunction(behavior[method])
);
return b[method].bind(b);
}
}
class Behavior1 {
foo() {
console.log("B1: foo");
}
foo2() {
console.log("B1: foo2");
this.getMethod("bar")();
}
setClient(client) {
this.client = client;
}
getMethod(method) {
return this.client.getMethod(method);
}
}
class Behavior2 {
foo() {
console.log("B2: foo");
this.getMethod("foo2")();
}
bar() {
console.log("B2: bar");
}
setClient(client) {
this.client = client;
}
getMethod(method) {
return this.client.getMethod(method).bind(this);
}
}
const c = new Composite();
c.add(new Behavior1());
c.add(new Behavior2());
c.getMethod("foo")();
c.getMethod("bar")();
// Output:
// B2: foo
// B1: foo2
// B2: bar
// B2: bar
Link to codesandbox: https://codesandbox.io/s/zen-poitras-56f4e?file=/src/index.js
You can review my other answer to see some of the issues and concerns with the previous approach. Here I've created a completely different version from the ground up. There is less code repetition and less tight coupling between the classes.
Behaviors no longer call methods directly and no longer store a reference to the client. Instead, they receive the client (or any object which call get and call methods) as an argument of their register method.
We define any object which can lookup and call methods as a MethodAccessor
interface MethodAccessor {
getMethod(name: string): () => void;
safeCallMethod(name: string): boolean;
}
We define any object that provides behaviors through a register method as a BehaviorWrapper. These objects can call functions from other objects by calling getMethod or safeCallMethod on the helper argument.
type KeyedBehaviors = Record<string, () => void>;
interface BehaviorWrapper {
register(helper: MethodAccessor): KeyedBehaviors;
}
A behavior which does not need instance variables could be a pure function rather than a class.
const functionBehavior = {
register(composite: MethodAccessor) {
return {
foo: () => console.log("B1: foo"),
foo2: () => {
console.log("B1: foo2");
composite.safeCallMethod("bar");
}
};
}
};
Class behaviors can make use of instance variables in their methods.
class ClassBehavior {
name: string;
constructor(name: string) {
this.name = name;
}
bar = () => {
console.log(`Hello, my name is ${this.name}`);
};
register() {
return {
bar: this.bar
};
}
}
There is some redundancy here when defining a method like bar separately rather than inline as an arrow function within the return object. The reason that I am having the methods come from register rather than using all class methods is so that I can have stricter typing on them. You could have methods in your class which do require args and as long as they aren't part of the register returned object then it's not a problem.
Our class Composite now stores its behaviors in a keyed object rather than an array. Newly added behaviors of the same name will overwrite older ones. Our getMethod is typed such that it always returns a method, and will throw an Error if none was found. I've added a new method safeCallMethod to call a method by name. If a method was found, it calls it and returns true. If no method was found, it catches the error and returns false.
class Composite implements MethodAccessor {
behaviors: KeyedBehaviors = {};
constructor(behavior?: BehaviorWrapper) {
if (behavior) {
this.add(behavior);
}
}
// add all behaviors from a behavior class instance
add(behavior: BehaviorWrapper): this {
this.behaviors = {
...this.behaviors,
...behavior.register(this)
};
return this;
}
// lookup a method by name and return it
// throws error on not found
getMethod(method: string): () => void {
const b = this.behaviors[method];
if (!b) {
throw new Error(`behavior ${method} not found`);
}
return b;
}
// calls a method by name, if it exists
// returns true if called or false if not found
safeCallMethod(method: string): boolean {
try {
this.getMethod(method)();
return true;
} catch (e) {
return false;
}
}
}
There's a lot that's not ideal about your setup. I might post a separate answer with an alternate setup, but for now I just want to show you how to convert your code to typescript.
Keep in mind that typescript errors exist to help you prevent runtime errors, and there are some genuine potential runtime errors that we need to avoid. If a Behavior calls getMethod before calling setClient to set this.client that will be a fatal error. If you try to call the returned method from getMethod on a Composite or a Behavior where the name didn't match a method that's another fatal error. And so on.
You choose to handle certain situations by throwing an Error with the expectation that it will be caught later on. Here I am preferring to "fail gracefully" and just do nothing or return undefined if we can't do what we want. The optional chaining ?. helps.
When defining an interface for a function argument, it's best to keep it to the minimum necessities and not require any extraneous properties.
The only thing that a Behavior requires of its Client is a getMethod method.
interface CanGetMethod {
getMethod(name: string): MaybeMethod;
}
We use the union of undefined and a void function in a few places, so I am saving it to an alias name for convenience.
type MaybeMethod = (() => void) | undefined;
The Composite calls setClient on its behaviors, so they must implement this interface.
interface CanSetClient {
setClient(client: CanGetMethod): void;
}
It also expects that its methods take zero arguments, but we can't really declare this with the current setup. It is possible to add a string index to a class, but that would conflict with our getMethod and setClient arguments which do require arguments.
One of the typescript errors that you get a bunch is `Cannot invoke an object which is possibly 'undefined', so I created a helper method to wrap a function call.
const maybeCall = (method: MaybeMethod): void => {
if (method) {
method();
}
};
In typescript, classes need to declare the types for their properties. Composite gets an array of behaviors behaviors: CanSetClient[]; while the behaviors get a client client?: CanGetMethod;. Note that the client must be typed as optional because it is not present when calling new().
After that, it's mostly just a matter of annotating argument and return types.
I have declared the interfaces that each class implements, ie. class Behavior1 implements CanGetMethod, CanSetClient, but this is not required. Any object fits the interface CanGetMethod if it has a getMethod property with the right types, whether it explicitly declares CanGetMethod in its type or not.
class Composite implements CanGetMethod {
behaviors: CanSetClient[];
constructor(behavior?: CanSetClient) {
this.behaviors = [];
if (behavior) {
this.add(behavior);
}
}
add(behavior: CanSetClient): this {
behavior.setClient(this);
this.behaviors.push(behavior);
return this;
}
getMethod(method: string): MaybeMethod {
const b = findLast(this.behaviors, (behavior) =>
isFunction(behavior[method])
);
return b ? b[method].bind(b) : undefined;
}
}
class Behavior1 implements CanGetMethod, CanSetClient {
client?: CanGetMethod;
foo() {
console.log("B1: foo");
}
foo2() {
console.log("B1: foo2");
maybeCall(this.getMethod("bar"));
}
setClient(client: CanGetMethod): void {
this.client = client;
}
getMethod(method: string): MaybeMethod {
return this.client?.getMethod(method);
}
}
class Behavior2 implements CanGetMethod, CanSetClient {
client?: CanGetMethod;
foo() {
console.log("B2: foo");
maybeCall(this.getMethod("foo2"));
}
bar() {
console.log("B2: bar");
}
setClient(client: CanGetMethod) {
this.client = client;
}
getMethod(method: string): MaybeMethod {
return this.client?.getMethod(method)?.bind(this);
}
}
const c = new Composite();
c.add(new Behavior1());
c.add(new Behavior2());
maybeCall(c.getMethod("foo"));
maybeCall(c.getMethod("bar"));

Prevent a call to a super method from happening multiple times in a proxy in JavaScript

I've written a TypeScript decorator/JavaScript mixin around a React Component called autodispose. Suppose that class A extends autodispose(Component) and class B extends A. The mixin ensures that A.componentWillUnmount() is called regardless of whether B.componentWillUnmount() calls super.componentWillUnmount() via a proxy.
(The code is in TypeScript, but the question pertains to JavaScript.)
export function autodispose<
T extends Class<React.Component>
>(Target: T) {
const ObservingComponent = class extends Target {
constructor(...args: any[]) {
super(...args);
// ... mixin setup ...
this.componentWillUnmount = new Proxy(this.componentWillUnmount, {
apply: (target, thisArg, fnArgs) => {
Reflect.apply(target, thisArg, fnArgs);
if (super.componentWillUnmount) {
super.componentWillUnmount();
}
this.__mixinCleanup();
},
});
}
componentWillUnmount() {
if (super.componentWillUnmount) {
super.componentWillUnmount();
}
this.__mixinCleanup();
}
private __mixinCleanup() {
// is a no-op if __mixinCleanup() has already been called
// ...
}
};
}
If B calls super.componentWillUnmount() then the proxy will call A's componentWillUnmount() twice--first by Reflect.apply(target, thisArg, fnArgs) and then immediately after that. I need a way to detect if the call to Reflect.apply() has already called super.componentWillUnmount() and prevent the second call.
I considered temporarily overriding super.componentWillUnmount with another Proxy which sets a flag that it's been called, but, unsurprisingly, you can't override super's methods.
If all else fails, I can just make sure that autodispose doesn't get called in the prototype chain twice, but this solution would be more ideal.
You don't need to (temporarily or otherwise) overwrite B's super.componentWillUnmount - you did define A.prototype.componentWillUnmount yourself! You can just set the flag in there:
export function autodispose(Target) {
return class ObservingComponent extends Target {
constructor(...args) {
super(...args);
// ... mixin setup ...
this._hasSuperBeenCalled = false;
let original = this.componentWillUnmount;
this.componentWillUnmount = function(...args) {
this._hasSuperBeenCalled = false;
original.apply(this, args);
if (!this._hasSuperBeenCalled)
throw new Error(`Warning: ${this.constructor.name}.componentWillUnmount forgot its super call`);
};
}
componentWillUnmount() {
this._hasSuperBeenCalled = true;
if (super.componentWillUnmount) {
super.componentWillUnmount();
}
// ...mixin cleanup
}
};
}

Static function inheritance

I've created a few classes as Controllers for my routes in Node.js and I'd like them to be able to work on a singleton basis (not required, it's an extra)
Main Controller class
class RouteController {
static getInstance() {
if (!RouteController.singleton) {
RouteController.singleton = new RouteController();
}
return RouteController.singleton;
}
}
Now I want to create a route for example viewing posts and I want that class to have the singleton pattern as well, but not cop
PostController class
class PostController extends RouteController {
}
If I do this and check it console.log(RouteController.getInstance() === PostController.getInstance()) it returns true when I want each (sub)class to have their own singleton instance.
How can I do this correctly?
One simple way would be to see if your singleton property is an instance of this. This will work even if you call RouteController.getInstance() before doing so on any of the derived classes.
class RouteController {
log() {
return 'Route';
}
static getInstance() {
if (!(this.singleton instanceof this)) {
// Only called twice so you can verify it's only initialized
// once for each class
console.log('Creating singleton...');
this.singleton = new this();
}
return this.singleton;
}
}
class PostController extends RouteController {
log() {
return 'Post';
}
}
console.log(RouteController.getInstance().log());
console.log(PostController.getInstance().log());
console.log(RouteController.getInstance().log());
console.log(PostController.getInstance().log());
The normal rules of functions apply. Hence you can use this inside the function to refer to the object the method was invoked on (the constructor function itself in this case):
class RouteController {
static getInstance() {
if (!this.singleton) {
this.singleton = new this();
}
return this.singleton;
}
}
However, this will still not work if RouteController.getInstance() is called directly, because then all subclasses inherit a static singleton property, causing the !this.singleton test to fail for each subclass.
There are multiple ways to solve this. One would be to use getOwnProperty instead of accessing it directly:
if (!this.getOwnProperty('singleton')) {
}

Categories

Resources