In the below code, How do I get access to the name of the function attempted to be called, and any params, in the catching function, in this example how can p.foo() (which is handled by the notFound function) print out the 'one','two','three' params as well.
the amount of parameters should not be hardcoded as in this example 3.
I have tried replacing:
return target['notFound'];
with a bunch of combinations of () and [] and passing 'arguments' and even 'prop', no luck.
also tried using .apply() still didnt get it to work.
var Foo = function () {
this.bar = function () {
return "found!";
}
this.notFound = function () {
return "method not found.";
}
};
var p = new Proxy(new Foo, {
get: function (target, prop) {
if (Object.keys(target).indexOf(prop) !== -1) {
return target[prop];
} else {
return target['notFound'];
}
}
});
console.log(p.bar()); // prints: found!;
console.log(p.foo("one","two","three")); // prints: method not found;
thanks in advance!
I think you're looking for
class Foo {
bar() {
return "found!";
}
notFound(name, args) {
return `method '${name}' not found, was called with [${args.join(', ')}].`;
}
}
var p = new Proxy(new Foo, {
get: function (target, prop) {
if (prop in target) {
return target[prop];
} else {
return function(...args) { return target.notFound(prop, args); };
}
}
});
console.log(p.bar()); // prints: found!;
console.log(p.foo("one","two","three")); // prints: method 'foo' not found, was called with [one, two, three]
Related
How can I get this code to work. I want to intercept all missing method calls on my function, and redirect them to a "catch all" kind of method on said function.
var foo=function(){
this.bar=function(){
return "found!";
}
this.not_found=function(){
return "method not found.";
}
}
var p = new Proxy(foo, {
get: function (target, prop) {
if (Object.keys(target).indexOf(prop) !== -1) {
// should call existing method.
//return target[prop]; <-- does not work.
}else{
// here i want to call "not_found() on foo() if prop doesnt exist
}
}
});
console.log(p.bar()); // should print out: found!;
console.log(p.foo()); // should print out: method not found;
thanks in advance!
foo is a function. As deepak said, if you create an object from it, it will work:
var Foo = function () {
this.bar = function () {
return "found!";
}
this.notFound = function () {
return "method not found.";
}
};
var p = new Proxy(new Foo, {
get: function (target, prop) {
if (Object.keys(target).indexOf(prop) !== -1) {
return target[prop];
} else {
return target['notFound'];
}
}
});
console.log(p.bar()); // prints: found!;
console.log(p.foo()); // prints: method not found;
Since Foo is acting like a constructor, I wrote it with a capital letter. Also, the convention is to use camel case for names. Took the liberty to change these.
A slightly cleaner version (using in):
var Foo = function () {
this.bar = function () {
return "found!";
}
this.notFound = function () {
return "method not found.";
}
};
var foo = new Foo();
var p = new Proxy(foo, {
get: function (target, prop) {
if (prop in target) {
return target[prop];
} else {
return target['notFound'];
}
}
});
console.log(p.bar()); // prints: found!;
console.log(p.foo()); // prints: method not found;
I want to check that my object properties and method or anything else is called or not? for example,
// functions
function app(){
return {
name : 'Md Tahazzot',
info : function(){
return this.name;
}
};
}
Now if I call this like app(), I mean In this case I am not called any of the object properties or methods. So, Is it possible to check this that I am called only the function nothing else like this app().name ?
You could return a Proxy. If the proxy's getters (or setters?) are ever called, then you know that something has been done other than simply call the function - something attempted to get or set a property on the returned object:
function app() {
const target = {
name: 'Md Tahazzot',
info: function() {
return this.name;
}
};
return new Proxy(target, {
get(target, prop) {
console.log('Get attempted');
return target[prop];
},
set(target, prop, newVal) {
console.log('Set attempted');
return target[prop] = newVal;
}
});
}
console.log('Creating "a":');
const a = app();
console.log('Creating "b":');
const b = app();
b.name;
console.log('Creating "c":');
const c = app();
c.foo = 'foo';
console.log(c.foo);
If you have to do this from outside the app, then apply the same logic after the object has been returned:
function app() {
return {
name: 'Md Tahazzot',
info: function() {
return this.name;
}
};
}
const obj = new Proxy(app, {
get(target, prop) {
console.log('Get attempted');
return target[prop];
},
set(target, prop, newVal) {
console.log('Set attempted');
return target[prop] = newVal;
}
});
console.log('Proxy created');
obj.name;
As functions are nothing but objects in JavaScript, you can create property on function itself to store any info at function level.
You could do something like this:
function app(){
app.callsCount = app.callsCount || 0;
app.callsCount++;
return {
name : 'Md Tahazzot',
info : function(){
return this.name;
}
};
}
And can be used like this:
app().name
app.callsCount // 1
app()
app.callsCount //2
Keep in mind, once function is called, the count is increased, if you want to increase count on inner function call you could do that too. However it would not be straight forward to know if a property is called after app function call.
Not exactly sure what exactly you are trying to achieve.
TL:DR; Is it possible to make a property of object to be invocable ( as a function ) only ?
What i mean by this
class Foo{
bar(value){
return value
}
}
let newFoo = new Foo()
console.log(newFoo.bar(123)) // should work fine as function is invoked
console.log(newFoo.bar) // here i need to throw or display an error instead of returning value
I tried to do this with Proxy and handler.get trap, but i have no clue how to capture whether it is a function call or just property access,
class Foo {
bar(value) {
return value
}
}
const proxied = new Proxy(new Foo(), {
get: function(target, prop, reciver) {
if (prop === 'bar') {
throw new Error('Bar is method need to be invoced')
}
return target[prop]
}
})
console.log(proxied.bar(true))
console.log(proxied.bar)
I have also checked handler.apply but this also doesn't seems to be of no use as this is a trap on function, not on property
class Foo {
bar(value) {
return value
}
}
const proxied = new Proxy(new Foo(), {
apply: function(target, thisArg, argumentsList) {
return target(argumentsList[0])
},
get: function(target, prop, reciver) {
if (prop === 'bar') {
throw new Error('Bar is method need to be invoced')
}
return target[prop]
}
})
console.log(proxied.bar(true))
console.log(proxied.bar)
No, this is not possible. There is no distinction between
const newFoo = new Foo()
newFoo.bar(123);
and
const newFoo = new Foo()
const bar = newFoo.bar;
Function.prototype.call.call(bar, newFoo, 123); // like `bar.call(newFoo, 123)`
// or Reflect.apply(bar, newFoo, [123]);
i.e. neither newFoo nor bar can distinguish these "from the inside". Now arbitrary things could happen in between the property access and the method call, and during the property access you cannot know what will happen next, so you cannot throw an exception prematurely. The method call might happen never (in newFoo.bar;), and there's no way to recognise that from newFoo alone.
The only approach would be to intercept all other accesses on newFoo and its properties, and throw after you detected a mischievous sequence; possibly having your "linter" check the sequence from the outside after the whole program ran:
const lint = {
access: 0,
call: 0,
check() {
console.log(this.access == this.call
? "It's ok"
: this.access > this.call
? "method was not called"
: "property was reused");
},
run(fn) {
this.call = this.access = 0;
try {
fn();
} finally {
this.check();
}
}
}
function bar(value) {
lint.call++; lint.check();
return value;
}
class Foo {
get bar() {
lint.check(); lint.access++;
return bar;
}
}
lint.run(() => {
const newFoo = new Foo;
newFoo.bar(123);
});
lint.run(() => {
const newFoo = new Foo;
newFoo.bar;
});
lint.run(() => {
const newFoo = new Foo;
const bar = newFoo.bar;
bar(123);
bar(456);
});
The better solution would probably to write your own interpreter for simple expressions, which would only allow method calls.
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.
Is there a way to make any function output a console.log statement when it's called by registering a global hook somewhere (that is, without modifying the actual function itself) or via some other means?
Here's a way to augment all functions in the global namespace with the function of your choice:
function augment(withFn) {
var name, fn;
for (name in window) {
fn = window[name];
if (typeof fn === 'function') {
window[name] = (function(name, fn) {
var args = arguments;
return function() {
withFn.apply(this, args);
return fn.apply(this, arguments);
}
})(name, fn);
}
}
}
augment(function(name, fn) {
console.log("calling " + name);
});
One down side is that no functions created after calling augment will have the additional behavior.
As to me, this looks like the most elegant solution:
(function() {
var call = Function.prototype.call;
Function.prototype.call = function() {
console.log(this, arguments); // Here you can do whatever actions you want
return call.apply(this, arguments);
};
}());
Proxy Method to log Function calls
There is a new way using Proxy to achieve this functionality in JS.
assume that we want to have a console.log whenever a function of a specific class is called:
class TestClass {
a() {
this.aa = 1;
}
b() {
this.bb = 1;
}
}
const foo = new TestClass()
foo.a() // nothing get logged
we can replace our class instantiation with a Proxy that overrides each property of this class. so:
class TestClass {
a() {
this.aa = 1;
}
b() {
this.bb = 1;
}
}
const logger = className => {
return new Proxy(new className(), {
get: function(target, name, receiver) {
if (!target.hasOwnProperty(name)) {
if (typeof target[name] === "function") {
console.log(
"Calling Method : ",
name,
"|| on : ",
target.constructor.name
);
}
return new Proxy(target[name], this);
}
return Reflect.get(target, name, receiver);
}
});
};
const instance = logger(TestClass)
instance.a() // output: "Calling Method : a || on : TestClass"
check that this actually works in Codepen
Remember that using Proxy gives you a lot more functionality than to just logging console names.
Also this method works in Node.js too.
If you want more targeted logging, the following code will log function calls for a particular object. You can even modify Object prototypes so that all new instances get logging too. I used Object.getOwnPropertyNames instead of for...in, so it works with ECMAScript 6 classes, which don't have enumerable methods.
function inject(obj, beforeFn) {
for (let propName of Object.getOwnPropertyNames(obj)) {
let prop = obj[propName];
if (Object.prototype.toString.call(prop) === '[object Function]') {
obj[propName] = (function(fnName) {
return function() {
beforeFn.call(this, fnName, arguments);
return prop.apply(this, arguments);
}
})(propName);
}
}
}
function logFnCall(name, args) {
let s = name + '(';
for (let i = 0; i < args.length; i++) {
if (i > 0)
s += ', ';
s += String(args[i]);
}
s += ')';
console.log(s);
}
inject(Foo.prototype, logFnCall);
Here's some Javascript which replaces adds console.log to every function in Javascript; Play with it on Regex101:
$re = "/function (.+)\\(.*\\)\\s*\\{/m";
$str = "function example(){}";
$subst = "$& console.log(\"$1()\");";
$result = preg_replace($re, $subst, $str);
It's a 'quick and dirty hack' but I find it useful for debugging. If you have a lot of functions, beware because this will add a lot of code. Also, the RegEx is simple and might not work for more complex function names/declaration.
You can actually attach your own function to console.log for everything that loads.
console.log = function(msg) {
// Add whatever you want here
alert(msg);
}