How to profiling all method in es6 class - javascript

I'm trying to "profiling" all function in class in javascript es6,
for that I created a container and I get from this container es6 class
instance wrapped in Proxy class. I think apply method can do that but in fact
when I try to call any method on that object apply method doesn't call
instead of this call the get method on the proxy.
I try to solve that with this code, but it isn't work (infinity loop)
get(target, property, receiver) {
if(typeof target[property] === 'function') {
let handler = new FunctionHandler(target, this.className, property);
handler.beforeCallList = this.beforeCallList;
handler.afterCallList = this.afterCallList;
handler.exceptionHandlingList = this.exceptionHandlingList;
this.count++;
if(this.count > 5)
process.exit();
let fn = target[property];
return new Proxy(fn, handler);
}
return target[property];
}
example:
class SomeClass {
someMethod() {
console.log('SomeClass::someMethod is called');
}
}
class Logger extends AbstractPostProcessor {
postProcessAfterInitialization(object, className) {
let proxy = this.getProxy(object, className);
proxy.vProxyHandler.beforeCallList.push((data) => {
console.log(`before call ${data.className}`);
});
proxy.vProxyHandler.afterCallList.push((data) => {
console.log(`after call ${data.className}`);
});
proxy.vProxyHandler.exceptionHandlingList.push((data) => {
console.log(`exception in ${data.className}`);
});
return proxy;
}
}
container.addPostProcessor(Logger);
const someClassObject = container.getObject('SomeClass');
someClassObject.someMethod();
result
before call SomeClass::someMethod
SomeClass::someMethod is called
after call SomeClass::someMethod
https://github.com/ashmna/v1-di/tree/master/src
please see in ProxyHandler.js

Related

How could I create an element wrapper in Javascript?

For pure educational and curiosity purposes, I am trying to create an element-wrapper object that allows me to tack-on my own properties and methods to an element. The behavior I'm trying to simulate is basically this:
// get a button element to wrap
const button = document.querySelector('button');
// some function that wraps new properties/methods around a given element
function wrap(element) {
this.customName = 'John';
this.customAge = 100;
this.printName = function() {
console.log(this.customName);
}
// ...
// ...somehow inherit element fields...
// ...
}
// wrap the button element
const customElement = new wrap(button);
// custom behavior:
console.log(customElement.customAge) // output => 100
customElement.printName() // output => 'John'
// legacy behavior
console.log(customElement.clientHeight) // output => client height
customElement.remove() // => should still call 'remove' on the element
So, here I should be able to add my own methods/properties but still access the original fields normally. Is this even possible?
I'm using a constructor function here as an example just to demonstrate the intended behavior, but I don't actually know if this would be relevant for the solution. I'm new to Javascript and I have done a ton of research on prototypes and classes, but I'm still confused on what approach I would take here.
Edit: As Brad pointed out in the comments, I also tried this implementation using classes:
class MyButton extends HTMLButtonElement {
constructor() {
super();
this.customName = 'John';
this.customAge = 100;
}
printName() {
console.log(this.customName);
}
}
const myBtn = new MyButton();
But this resulted in the error:
Uncaught TypeError: Illegal constructor
I haven't test this, but maybe something like this:
// get a button element to wrap
const button = document.querySelector('button');
// some function that wraps new properties/methods around a given element
function wrap(element) {
Object.defineProperties(element, {
customName: {value:"John"},
customAge: {value:100},
printName:{value: () => console.log(element.customName)}
})
return element
}
// wrap the button element
const customElement = wrap(button);
// custom behavior:
console.log(customElement.customAge) // output => 100
customElement.printName() // output => 'John'
// legacy behavior
console.log(customElement.clientHeight) // output => client height
customElement.remove() // => should still call 'remove' on the element
<button>Hello world!</button>
Another method could be used is wrap element into proxy()
This will allow return custom data if property doesn't exist and send notifications when properties changed:
const customElement = function (element, properties = {})
{
this.element = element;
this.customName = 'John';
this.customAge = 100;
this.printName = function() {
console.log(this.customName);
}
//override default properties
for(let i in properties)
{
if (i in element)
element[i] = properties[i];
else
this[i] = properties[i];
}
return new Proxy(this, {
get(target, prop)
{
if (prop in target.element) //is property exists in element?
{
if (target.element[prop] instanceof Function)
return target.element[prop].bind(target.element);
return target.element[prop];
}
else if (prop in target) //is property exists in our object?
return target[prop];
else
return "unknown property"; //unknown property
},
set(target, prop, value, thisProxy)
{
const oldValue = thisProxy[prop];
if (prop in target.element)
target.element[prop] = value;
else
target[prop] = value;
// send notification
target.element.dispatchEvent(new CustomEvent("propertyChanged", {
detail: {
prop,
oldValue,
value
}
}));
}
});
}
const button = new customElement(document.createElement("button"), {customName: "Not John"});
button.addEventListener("propertyChanged", e =>
{
console.log("property changed", e.detail);
});
button.printName();
console.log("age:", button.customAge);
console.log("height:", button.clientHeight);
console.log("blah:", button.blah);
button.blah = "ok";
console.log("blah:", button.blah);

How to always make "this" keyword to reference the parent class (bind child methods to parent class)?

here is the simplest form of my problem:
class Service1 {
constructor() { this.name = 'service1' }
getThisName() { console.log('Name: ' + (this && this.name)) }
}
const service1 = new Service1();
service1.getThisName() // 'service1' => :)
function mapper(fn, ...params) {
this.name = 'mapper';
// ...params can be parsed or generated here
fn(...params);
}
mapper(service1.getThisName) // undefined => :'(
I know I can fn.bind(service1) in the mapper function to solve the problem, but as fn is dynamic, I would prefer not to do that.
I have tried searching on how to get the parent class from child method but get no results.
I want mapper to be able to call a method of a class (or object) without loosing the this reference in a readable and straightforward way if possible. mapper is always called in the same context.
Is there a way in javascript to solve this problem ?
What I have tried
function mapper(fn, serviceClass) {
fn.bind(serviceClass)();
}
mapper(service1.getThisName, service1) // OK but is not readable and seems hacky
function mapper(serviceClass, fnName) {
serviceClass[fnName]();
}
mapper(service1, 'getThisName') // OK but autocompletion in the IDE don't work
function mapper(fn) {
fn();
}
mapper(service1.getThisName.bind(service1)) // the "best practice" but in my case not enougth readable
Real use case context
In the real use case scenario, the mapper is called api2service. As the name suggests, it is used with expressJs to map api routes to services. Here is a simplified version of the code:
app.get(
'get/all/users', // api endpoint
api2service(
userService.getAll, // getAll take filter as the first param
['req.query'] // req.query is the filter and is mapped AND parsed as the first param of the service function. Eg: {name: 'blah'}
)
)
That code is repeated a lot of time and always called in the same context, that's why I need something readable over the strict respect of good practices.
Until the bind operator proposal is implemented, there's not much you can do about this. Apart from your attempts, you can automatically bind methods at construction time (see also https://github.com/sindresorhus/auto-bind):
function autoBind(obj) {
let proto = Object.getPrototypeOf(obj);
for (let k of Object.getOwnPropertyNames(proto)) {
if (typeof proto[k] === 'function' && k !== 'constructor')
obj[k] = proto[k].bind(obj)
}
}
class Service1 {
constructor() {
this.name = 'service1'
autoBind(this);
}
getThisName() { console.log('Name: ' + (this && this.name)) }
}
function mapper(fn) {
fn();
}
let srv = new Service1
mapper(srv.getThisName)
or use a binding Proxy:
function Bound(obj) {
return new Proxy(obj, {
get(target, prop) {
let el = target[prop];
if(typeof el === 'function')
return el.bind(target)
}
})
}
class Service1 {
constructor() {
this.name = 'service1'
}
getThisName() { console.log('Name: ' + (this && this.name)) }
}
function mapper(fn) {
fn();
}
let srv = new Service1
mapper(Bound(srv).getThisName)

How to intercept a function call in JavaScript to grab the function call name

I wasn't sure how to word the title so I will go into more detail.
What I want to do is have some object called car.
The object car contains two objects called tire and engine.
What I want to happen is be able to say,
car.start();
Then I want the car object to be able to check both tire and engine to see if it contains a function with that name then call it.
So in summary
I want to be able to call an object and have that object called pass it onto whoever implemented that function call.
I looked at the proxy pattern but I don't see how I can dynamically have function calls passed on from an object to a nested object.
Any ideas would be appreciated. Thanks!
Example Code
function engine() {
return {
start: () => console.log('start')
}
}
function tire() {
return {
getWidth: () => console.log('get width')
}
}
function car() {
this.tire = new tire();
this.engine = new engine();
// Pass on function calls made to car over to tire or engine depending on who implemented that function call.
}
// This should print start.
car.start();
**PS. ** I know I can hard code the function calls and have it pass through but I am trying to do this dynamically so I don't have to declare every single function that can be called. So I want to do this dynamically.
Convert them to actual classes then copy properties from their prototypes:
class Engine {
start() {
console.log("start");
}
}
class Tire {
getWidth() {
console.log("get width");
}
}
class Car {
constructor() {
this.tire = new Tire();
this.engine = new Engine();
}
}
for (const key of Object.getOwnPropertyNames(Engine.prototype)) {
if (key !== "constructor") {
Car.prototype[key] = function(...args) { return this.engine[key](...args); };
}
}
for (const key of Object.getOwnPropertyNames(Tire.prototype)) {
if (key !== "constructor") {
Car.prototype[key] = function(...args) { return this.tire[key](...args); };
}
}
const car = new Car();
car.start();
car.getWidth();
Addressing this: "I looked at the proxy pattern but I don't see how I can dynamically have function calls passed on from an object to a nested object."
It is possible you missed checking for the 'undefined' method of the proxied class. In the code below note target[prop] === undefined - this check determines if the dynamic method exists on the target. If not, then look at the prop and call the method from the embedded object.
If you don't want to enumerate the methods from the embedded object, you can introspect them to find the object containing the method (not shown here).
This should demonstrate that a Proxy object can, indeed be used to accomplish your goal.
function Engine() {
this.start = () => {
console.log('started');
}
}
function Tire() {
this.getWidth = () => {
console.log('got width');
}
}
function Car() {
this.tire = new Tire();
this.engine = new Engine();
}
const carProxy = {
get: function(target, prop, receiver) {
if (target[prop] === undefined) {
if (prop === "getWidth") {
console.log('getting tire width');
return target.tire.getWidth;
} else if (prop === "start") {
console.log('starting');
return target.engine.start;
} else {
return () => {
console.log('Undefined function');
}
}
}
}
};
const target = new Car()
const car = new Proxy(target, carProxy);
car.start();
car.getWidth();

Intercept function calls in javascript

What's the equivalent of the __call magic method from PHP ?
I was under the impression that Proxy can do this, but it can't.
class MyClass{
constructor(){
return new Proxy(this, {
apply: function(target, thisArg, args){
console.log('call', thisArg, args);
return 'test';
},
get: function(target, prop){
console.log('get', prop, arguments);
}
});
}
}
var inst = new MyClass();
console.log(inst.foo(123));
get seems to work because I see "get foo", but apply does not. I get is not a function error.
apply actually handles a function call to the object itself, i.e. if you do new Proxy(someFunction, { apply: ... }), apply would be called before someFunction is called.
There is nothing for trapping a call to a property, because this would be superfluous – get already handles when a property is returned. You can simply return a function that then produces some debug output when called.
class MyClass{
constructor(){
return new Proxy(this, {
get: function(target, prop) {
return function() {
console.log('function call', prop, arguments);
return 42;
};
}
});
}
}
var inst = new MyClass();
console.log(inst.foo(123));
This another way of achieving what you have requested.
class MyClass{
constructor(){
return new Proxy(this, {
get(target, propKey, receiver) {
const origMethod = target[propKey];
return function (...args) {
let result = origMethod.apply(this, args);
console.log(propKey + JSON.stringify(args)
+ ' -> ' + JSON.stringify(result));
return result;
};
}
});
}
foo = (x) => {
return x + 1;
};
}
var inst = new MyClass();
console.log(inst.foo(123));
Yeah Proxy can do that, but even when trapping methods you have to use get of Proxy.
Then here I also executes your real method, but I don't know if you want to mock it.
class MyClass {
constructor() {
return new Proxy(this, {
get(target, prop, receiver) {
if (typeof target[prop] !== "function") {
return "etcetcetc";
}
return function(...args) {
console.log('call', args);
return target[prop]();
};
}
});
}
foo() {
console.log('I am foo!');
}
}
var inst = new MyClass();
inst.foo(123);
As you can see, if you are calling a method of your instance, I will intercept it, and then return your original method execution.
If you are accessing an attribute of your instance, I will return always a mocked string.
Then of course change it with the behavior that you want to.

Publish subscriber pattern using instance based decorators 'this' is always undefined

class Observable {
constructor() {
this.handlers = [];
}
publish(value) {
this.handlers.forEach(handler => {
handler(value);
});
}
subscribe(callback) {
this.handlers.push(callback);
}
}
const concreteObserver = new Observable();
function Subscribe(observable) {
return function functionDescriptor(target, propertyKey, descriptor) {
observable.subscribe(target[propertyKey]);
return descriptor;
}
}
class MyClass {
constructor(){
this.x = 5;
}
#Subscribe(concreteObserver)
subsribeToValue(value) {
console.log(this.x); // undefined
}
}
As you can see, the subscribe function is called each time, someone calls concreteObserver.publish() however, when you call observable.subscribe(target[propertyKey]); then 'this' becomes undefined.
I also tried overriding the descriptor getter, and calling that one, but i still get undefined. On classes i was able to wrap a function by calling target.prototype.functionName.
This works when i know what the function name will be called, but the function name for #Subscribe can be arbitrary, so i can't use it on a class level decorator unless i use Reflection to detect all the annotations of the class.
EDIT
Tried so far
observable.subscribe(target[propertyKey].bind(this));
which returns undefined, subscribe has the right context in this case.
observable.subscribe(data => descriptor.value.apply(this, data)); also has 'this' as undefined
descriptor.value = function(){
console.log(this); //undefined
}
descriptor.get = function(){
console.log(this); //undefined
}
The solution i came up with. Since it is only possible to get the instance of a class in the class decorator, then that is where this can be used properly, in the the subscribe function i tell what function i should subscribe to, then in the ClassDecorator i iterate through each method to determine if they have __subscribeFunction in their prototype and thus subscribe to the method while binding instance
class Observable {
constructor() {
this.handlers = [];
}
publish(value) {
this.handlers.forEach(handler => {
handler(value);
});
}
subscribe(callback) {
this.handlers.push(callback);
}
}
const concreteObserver = new Observable();
function ClassDecorator(target) {
const originalTarget = target;
const Override = function (...args) {
const instance = originalTarget.apply(this, args);
Object.values(instance.__proto__).forEach(method => {
const observableFunction = method.prototype.__subscribeFunction;
if (observableFunction) {
observableFunction.subscribe(method.bind(instance));
}
});
return instance;
};
Override.prototype = originalTarget.prototype;
customElements.define(elementName, target);
return Override;
}
function Subscribe(observable) {
return function functionDescriptor(target, propertyKey, descriptor) {
target[propertyKey].prototype.__subscribeFunction = observable;
}
}
#ClassDecorator
class MyClass {
constructor(){
this.x = 5;
}
#Subscribe(concreteObserver)
subsribeToValue(value) {
console.log(this.x); // 5
}
}
This doesn't work because the decorator is called when the class itself is constructed, but before any instance is created. Since there's no instance, there can't be a this – you only have access to the prototype, but class properties aren't on the prototype (unlike methods).
You can verify this using this example:
function Example() {
console.log("#Example initialized");
return function exampleDescriptior(target, propertyKey, descriptor) {
console.log("#Example called");
}
}
console.log("Before declaring class");
class Test {
#Example()
public test() {}
}
console.log("After declaring class");
console.log("Before creating instance");
const test = new Test();
console.log("After creating instance");
console.log("Before calling method");
test.test();
console.log("After calling method");
which yields the output
Before declaring class
#Example initialized
#Example called
After declaring class
Before creating instance
After creating instance
Before calling method
After calling method
That said, what you can do is write another decorator applied on, say, class level which proxies the constructor. If your #Subscribe annotation stores some meta-data on the prototype, the class decorator could then look for it and do the actual wiring. So getting something like
#AutoSubscribe()
class MyClass {
#Subscribe(observer)
subscribe(value) {
console.log(this.x);
}
}
to work should be possible. In fact, you could maybe even get rid of the second decorator by proxying the constructor from the #Subscribe decorator, but you'd still have to store metadata that you can look through during instantiation.

Categories

Resources