Make object or class property only invocable - javascript

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.

Related

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();

How to check objects properties or functions called or not in javascript?

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.

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.

Using ES6 Proxy results in any function call "is not a function"

I was trying to be a sneaky developer, and I done got myself lost in ES6 Proxies. Basically I wanted to capture any get or set in a property from another class I wrote and make sure they are stored somewhere else besides the object. It looks like this:
'use strict'
class MyObject()
{
superAwesomeFunction(){//super awesome code goes here}
}
const myProxyObject = new Proxy(MyObject, {
get: function(target, name, receiver)
{
if([CONDITIONS THAT MEET MY DEMANDS])
{
// deliver property values if they are
// my super cool database (if they exist)
}
// else just ya know, forward it to the actual MyObject
// property, if it's an actual property on MyObject
return target[name];
},
set: function(target, name, value)
{
if([CONDITIONS THAT MEET MY DEMANDS])
{
// set property values if they are
// to super cool database
}
else
{
// just set the value to the property, which
// gets created if it doesn't exist (WHICH IS SO COOL)
target[name] = value;
}
return true;
}
Okay the cool part about this? you can do something like:
// property that doesn't exist (yet)
console.log(myProxyObject.wholivesinapineappleunderthesea);
// but wait for it...
myProxyObject.wholivesinapineappleunderthesea = 'spongebob';
// bam! now it exists on the object, AND lives in my DB!
console.log(myProxyObject.wholivesinapineappleunderthesea);
Which, while a lot of work for something dorky, i cannot explain how happy it makes me feel. However, there is a problem with this. Remember that superAwesomeFunction() I put in MyObject()? Well whenever I try to call it now ES6 gives me this load of sadness:
myProxyObject.superAwesomeFunction is not a function
ES6 sits on a throne of LIES! It's totally there right? Okay so I'm pretty sure I'm trapping something wrong, because when I debug I see that the get section of the Proxy is actually picking up the superAwesomeFunction call (which makes sense as superAwesomeFunction is a property that contains a function (){})
HERE'S MY QUESTION: Does anybody know of any solution that will let me keep my ridiculous on the fly properties and still call my superAwesomeFunction()? Is it the apply trap?
To extend on Sam's answer.
The following succeeds:
function Foo() {
this.bar = function() { console.log('baz'); }
}
let foo = new Proxy(new Foo(), {});
foo.bar();
However, when the get trap is added to the handler, "is not a function" is thrown:
function Foo() {
this.bar = function() { console.log('baz'); }
}
let foo = new Proxy(new Foo(), {
get: function(target, prop) {}
});
foo.bar();
The solution here is to add a typeof check for default handling of the target's existing functions:
function Foo() {
this.bar = function() { console.log('baz'); }
}
let foo = new Proxy(new Foo(), {
get: function(target, prop) {
if (typeof target[prop] === 'function') {
return target[prop].bind(target);
}
}
});
foo.bar();
The following articles gives some more examples and provides nice boilerplate to use with Proxy handlers: Safely Extending The JavaScript Set Object Using Proxies
It is a little syntax error there ... you are wrapping the proxy around the raw class, not an object.
Try:
const myProxyObject = new Proxy(new MyObject(), { ... });
This should be ok. For example:
> class Blah {
... run() {
..... console.log('yay')
..... }
... }
[Function: blah]
> myProx = new Proxy(new Blah(), {});
blah {}
> myProx.run()
yay
Full code for answer is here, thank you everyone!
'use strict'
class MyObject()
{
superAwesomeFunction(){
console.log("this works!");
}
}
const myProxyObject = new Proxy( new MyObject(), {
get: function(target, name, receiver)
{
if([CONDITIONS THAT MEET MY DEMANDS])
{
// do something cool for yourself
}
return target[name];
},
set: function(target, name, value)
{
if([CONDITIONS THAT MEET MY DEMANDS])
{
// do something cool for yourself
}
else
{
// just set the value to the property, which
// gets created if it doesn't exist (WHICH IS SO COOL)
target[name] = value;
}
return true;
}
});

Override default get in javascript class such as __get in php

I'm building a javascript library and I would like to be able to do exactly like the PHP's __get does.
My library has a attributes property which stores each model's attributes. Now, I am force to get an attribute using a .get method. But I would be able to do it with a getter. Let's says that User extends my model class.
let instance = new User({firstname: 'John', lastname: 'Doe'});
console.log(instance.get('firstname')); // gives me 'John'
I want to be able to do instance.firstname which will call the .get method passing 'firstname' as parameter. In PHP you can do it that way : http://php.net/manual/fr/language.oop5.overloading.php#object.get
Is this something possible?
Thank you all
This is easy using ES 2015 classes:
class Foo {
constructor () {
this._bar = null;
}
get bar () {
doStuff();
return this._bar;
}
set bar (val) {
doOtherStuff();
this._bar = val;
return this;
}
};
var foo = new Foo();
foo.bar = 3; // calls setter function
console.log(foo.bar); // calls getter function
here's the (simplified) output from babel:
var Foo = function () {
function Foo() {
this._bar = null;
}
_createClass(Foo, [{
key: "bar",
get: function get() {
doStuff();
return this._bar;
},
set: function set(val) {
doOtherStuff();
this._bar = val;
return this;
}
}]);
return Foo;
}();
Note that this isn't just for classes, any arbitrary object can have these:
var baz = {
get qux() {
// arbitrary code
},
set qux(val) {
// arbitrary code
}
};
Source.
EDIT
What you want is possible but only in native ES 6 environments, as Proxy cannot be polyfilled.
var getter = function(target, property, proxy) {
console.log(`Getting the ${property} property of the obj.`);
return target[property];
};
var setter = function(target, property, value, proxy) {
console.log(`Setting the ${property} property to ${value}.`);
target[property] = value;
};
var emptyObj = {};
var obj = new Proxy(emptyObj, {
get: getter,
set: setter
});
obj.a = 3; // logs 'Setting the a property to 3'
var foo = obj.a; // logs 'Getting the a property of the obj'
Quite simply assign the properties in a loop:
User = function (attrs) {
for (var name in attrs) {
this[name] = attrs[name];
}
}
User.prototype = {
// further methods
}
Using the ES6 class syntax, - I have to admit I do not see the point of writing things this way:
class User {
constructor (attrs) {
for (var name in attrs) {
this[name] = attrs[name];
}
}
// further methods
}
Remember: the second syntax is exactly what happens with the first one, only with some sugar on top.

Categories

Resources