For example, if I have this handler/proxy (from the MDN example)...
var handler = {
get: function(target, name){
return name in target?
target[name] :
37;
}
};
var p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;
console.log(p.a, p.b); // 1, undefined
console.log('c' in p, p.c); // false, 37
is it possible to probe the proxy, p, in some way that allows me to get the handler object back.
Something along the lines of:
p.__handler__ // returns handler object -> Object {get: handler.get(), set: handler.set(), ...}
p.__handler__.get // returns get prop/fn of handler -> function(target, name){ ...}
Obviously, the various traps set up in the handler are still "known" to the proxy, but is there a clear-cut way to return them/ the handler from the proxy itself? If so, how?
I have no specific use-case for this at the moment, but I could see this being useful if you wanted to dynamically change a handler/traps after you already have a proxy.
ECMAScript provides no way to access the internal [[ProxyHandler]] nor [[ProxyTarget]] slots.
Some implementations may provide some non-standard ways, but don't take it for granted.
For example, on Firefox privileged code, you can know if an object is a proxy using
Components.utils.isProxy(object);
I proposed implementing similar methods to expose the [[ProxyHandler]] and [[ProxyTarget]]. They told me to implement them in Debugger.Object instead of Components.utils.
When the patch lands, it will be possible to use something like
Components.utils.import('resource://gre/modules/jsdebugger.jsm');
var Cc = Components.classes;
// Add a debugger to a new global
var global = new Components.utils.Sandbox(
Cc["#mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal),
{ freshZone: true }
);
addDebuggerToGlobal(global);
var dbg = new global.Debugger().addDebuggee(this);
// Create a debugger object of your object, and run proxy getters
var dbgObj = dbg.makeDebuggeeValue(object);
if(dbgObj.isProxy) { // a boolean
dbgObj.proxyHandler.unsafeDereference(); // the [[ProxyHandler]]
dbgObj.proxyTarget.unsafeDereference(); // the [[ProxyTarget]]
}
Add a "special" self descriptor property to getOwnPropertyDescriptor
const target = {
//Fns ..
//Props ...
};
const handler = {
getOwnPropertyDescriptor(target, prop) {
if(prop == "[[handler]]"){
return { configurable: true, enumerable: true, value: this };
}
return undefined;
},
prop1: 'abcd'
};
const proxy = new Proxy(target, handler);
console.log(Object.getOwnPropertyDescriptor(proxy, '[[handler]]').value.prop1);
I could see this being useful if you wanted to dynamically change a handler/traps after you already have a proxy
If you just want to add handlers over the (proxied) object you already have access to: you could achieve this by creating a new Proxy that handles the specific traps you want to change, eg:
let newProxyWithDifferentGet = new Proxy(originalProxy, {
get: (target, key){ ... }
}
If you wanted to access the original Proxy's target:
If you are the original Proxy's author, you can just do something like this when you construct it:
let openedProxy = new Proxy(Object.assign(target, {
_originalHandler: handler,
_originalTarget: target
}), handler)
If you're not the author, then whether or not that original target should be available to users is the decision of whoever wrote that original Proxy. If you disagree with that author about their encapsulation, that's a social problem, not a technical one, and this is not specific or unique to ES6's Proxies. If you're consuming open-source code, send a PR upstream explaining why you think the original target should be available to users, or just fork their code with your changes and use that, merging their updates to the original repo as you go.
Related
I have a global variable and its type is String:
window.my_global_var = 'string';
It might be changed by some external-loaded JavaScript files or an AJAX request. So I want to watch it and invoke a callback when it's updated.
I searched a while but found Object.observe is already deprecated. I found an answer but it is better used to observe an object, not a String window.variable.
The worst approach would be using a setInterval to watch it but I guess it's too stupid.
Is there any good way to do this?
You can use Object.defineProperties on window:
function onValueUpdate(my_global_var) {
// Some arbitrary logic you want to execute on callback
console.log(`my_global_var was updated: ${my_global_var}`);
}
Object.defineProperties(window, {
_my_global_var: {
value: 'string',
writable: true
},
my_global_var: {
get: function() {
return this._my_global_var;
},
set: function(val) {
this._my_global_var = val;
onValueUpdate(this._my_global_var);
}
}
});
window.my_global_var = 'New string';
When you access window.my_global_var it behaves exactly as the property of type String would. But when you set it you can adjust it to use any additional logic.
Function onValueUpdate needs to be public (or you can use a public method instead).
There’s a warning against this approach in the answer you’ve found though:
I'd not go with getters/setters solution - it's complicated, not scalable and not maintainable.
So if you need scalability you probably should look for some library that can do that. Otherwise this should work just as well.
You could wrap the global object in a proxy with a set handler. You would need to pass the proxy around your program, rather than relying implicitly on the global object, however.
const handler = {
set(obj, prop, value) {
if (prop === 'foo')
console.log(`Property updated with value: ${value}!`)
return Reflect.set(...arguments)
}
};
const proxy = new Proxy(window, handler);
proxy.foo = 1 // "Property updated with value: 1!"
proxy.bar = 2
console.log(foo) // 1
console.log(bar) // 2
Let me start by saying this is more of a curiosity question because, as you will see, I was able to achieve the desired functionality.
However, given that javascript is a super flexible language, I would like to see what other devs might think about this problem:
I have an instance of a class which is returned from a vendor function:
const connection = vendorDatabaseLib.createConnection();
Now, I would like to create a decorator which will add functionality to the connection class, for example, reconnection logic.
Lets call it PersistentConnection. Apart from my added custom functions I would like an instance of PersistentConnection to forward all function calls to the original Connection instance. And in some functions override the behaviour.
I could of course implement all Connection's functions explicitly and forward them to the inner object but there might be lots of these functions, so I quickly discarded this idea.
So here are my ideas of how to achieve this:
Monkey patching 🐒, Instead of a decorator I can create a PersistentConnection class which inherits from the vendor Connection and then patch the vendor vendorDatabaseLib.createConnection function to return PersistentConnection with all my desired added functionality. Tempting, but bad.
Create a decorator which iterates over the Connection functions and creates forwards dynamically, something like:
class PersistentConnection{
constructor(connection){
this._connection = connection;
// Iterate through all functions
for (prop in this._connection){
if(typeof(this._connection[prop]) === 'function'){
// Create functions dynamically for each forward
this[prop] = (...args) => {
this._connection[prop](...args);
}
}
}
}
// This is the added logic
reconnect(){
// Custom logic
}
}
Set the Connection instance to be a the prototype of PersistentConnection's instance:
function persistenChannel(channel){
const persistentChannel = {};
Object.setPrototypeOf(persistentChannel, channel);
persistentChannel.reconnect = () => {
// custom logic
}
}
This is the most "automatic" way I could think of.. But it just down right ugly, and the custom functions need to be declared each time an instance is created.
I still feel like I'm missing something, something like Ruby's magical method_missing (or pythons __getattr__) function which is called just before a method is missing exception is thrown and lets you define "safety net" logic (like delegating all calls to the inner _connection object.
Is there a better way to achieve this functionality?
Thanks a lot [=
Lets start from what we have. In any case, most of the functionaliy will be performed by vendor object. We do not know details realization so we can't rely that this object has no state. This mean, that in any case we need new connection object for the new persistentConnection. This can be achieved with proxy object
Lets try to do this:
function Connection() {
this.connect = () => console.log('connected by Connection class');
this.disconnect = () => console.log('disconnected by Connection class');
}
function persistantConnectionFactory() {
function PersistentConnection() {
this.checkConnection = () => console.log('no connection');
}
const instance = new PersistentConnection();
const proxy = new Proxy(instance, {
get: function (target, name) {
if (!(name in target)) {
console.log('adding new prototype')
Object.setPrototypeOf(instance, new Connection())
}
return target[name];
}
});
return proxy;
}
var c = persistantConnectionFactory();
c.checkConnection();
c.connect();
Does this solution good? I think - not. Without very good reasons this adds complexity without any value. Prototype should be enough.
I'm using a Proxy to detect when an object is modified (and then I save it to disk). This works great for simple properties of the proxied object, but fails on modification of object properties.
var obj = {
p1 = "Hello",
a1 = []
}
var dirtyHandler = {
set: function(obj, prop, value) {
markDirty(obj);
obj[prop] = value;
return true;
}
};
var proxied = new Proxy(obj, dirtyHandler);
proxied.p1 = "World"; // <-- proxy detects modification
proxied.a1.push({'foo': 3}); // <-- proxy does not detect modification
Does anyone know how to recursively detect any modification in my object (a1.push(...), a1[0].foo = 4, etc.)?
Here's how I ended up solving this for my use case.
First I add a proxy for all the known objects I care about (not shown). In the handler for the proxy on every set call I check if the value is already a proxy and if not, substitute one:
var DirtyHandler = function(root) {
this.root = root;
this.set = (obj, prop, value) => {
if (!dirtyIgnores[prop]) {
debug('Dirty: ' + prop + ' of ' + obj.commitId);
markDirty(this.root);
}
if (value && typeof value === 'object') {
value = new Proxy(value, this);
}
obj[prop] = value;
return true;
};
}
I published a library on GitHub (Observable Slim) that recursively iterates through a target object and applies a Proxy on all objects. It enables you to monitor all changes that occur under a single target object no matter how deeply nested they are. It also has a few extra features:
Reports back to a specified callback whenever changes occur.
Will prevent user from trying to Proxy a Proxy.
Keeps a store of which objects have been proxied and will re-use existing proxies instead of creating new ones (very significant performance implications).
Allows user to traverse up from a child object and retrieve the parent.
Written in ES5 and plays nice with the Proxy Polyfill so it can be deployed in older browsers fairly easily.
Please feel free to take a look and hopefully contribute as well!
As a somewhat hacky method of disabling functionality, I came up with the idea of using some javascript like this:
//fakefrob.js
var frob = function () {
return {
myFunc: function(){},
myFunc1: function(){},
myFunc2: function(){return 2;},
myFunc3: function(){},
myFunc4: function(){}
};
}();
In this example, the real frob has implementations of these functions. Obviously this is a hack (the functions mostly do nothing or have placeholder return values). If I add foobar to realfrob.js, I must add an empty implementation of foobar to fakefrob.js. Is there a way (ideally without using a library) to write fakefrob.js such that frob.foobar acts like foobar was defined as function(){};, without actually adding foobar to fakefrob.js?
Is there a way (ideally without using a library) to write fakefrob.js such that frob.foobar acts like foobar was defined as function(){};, without actually adding foobar to fakefrob.js?
You mean, a catch-all property, mapped to a no-op function? No, JavaScript doesn't (currently) have catch-all properties at all. (I believe some mechanism for them is being considered, as part of the proxy stuff coming down the pike at some stage.)
If you have access to the real frob in your code, though, your fakefrob can be generated completely automatically:
// Assumes `frob` already exists
(function() {
var mock;
var name;
// Build the mock, with a function for each function
// on the real object.
mock = {};
for (name in frob) {
if (typeof frob[name] === "function") {
mock[name] = function() { };
}
}
// Replace the original
frob = mock;
})();
And of course, if you wanted to copy the non-function properties, you could do that at the same time.
Note that I intentionally did not include hasOwnProperty in the loop above, as I assume you want your mock to include functions even if frob inherits them from a prototype.
If frob inherits some functions and you wanted your mock to exhibit the same hasOwnPropety behavior for them as the original frob, you can get more creative:
// Assumes `frob` already exists
(function() {
var mock;
var mockProto;
var name;
function FakeFrob() {
}
mockProto = FakeFrob.prototype;
// Build the mock, with a function for each function
// on the real object.
mock = new FakeFrob();
for (name in frob) {
if (typeof frob[name] === "function") {
if (frob.hasOwnProperty(name)) {
mock[name] = function() { };
}
else {
mockProto[name] = function() { };
}
}
}
// Replace the original
frob = mock;
})();
Now, if the original frob had its own foo (frob.hasOwnProperty("foo") is true) but an inherited bar (frob.hasOwnProperty("bar") is false), hasOwnProperty on your mock would say exactly the same thing.
As T.J. Crowder suggests, supported browsers can use a proxy object to accomplish this:
var frob = new Proxy({}, {get: function(){return function(){}}})
How this works:
This creates a proxy which intercepts all property getters, replacing the result with an empty function. Normally, a proxy object is used to to intercept and eventually forward calls to our target (here, the target is {}). However, this code blindly returning an empty function, completely ignoring the target.
For instance this code:
function stuff() {
this.onlyMethod = function () {
return something;
}
}
// some error is thrown
stuff().nonExistant();
Is there a way to do something like PHP's __call as a fallback from inside the object?
function stuff() {
this.onlyMethod = function () {
return something;
}
// "catcher" function
this.__call__ = function (name, params) {
alert(name + " can't be called.");
}
}
// would then raise the alert "nonExistant can't be called".
stuff().nonExistant();
Maybe I'll explain a bit more what I'm doing.
The object contains another object, which has methods that should be accessible directly through this object. But those methods are different for each object, so I can't just route them, i need to be able to call them dynamically.
I know I could just make the object inside it a property of the main object stuff.obj.existant(), but I'm just wondering if I could avoid it, since the main object is sort of a wrapper that just adds some functionality temporarily (and makes it easier to access the object at the same time).
Well, it seems that with harmony (ES6), there will be a way, and it's more complicated compared to the way other programing languages do it. Basically, it involves using the Proxy built-in object to create a wrapper on the object, and modify the way default behavior its implemented on it:
obj = new Proxy({},
{ get : function(target, prop)
{
if(target[prop] === undefined)
return function() {
console.log('an otherwise undefined function!!');
};
else
return target[prop];
}
});
obj.f() ///'an otherwise undefined function!!'
obj.l = function() {console.log(45);};
obj.l(); ///45
The Proxy will forward all methods not handled by handlers into the normal object. So it will be like if it wasn't there, and from proxy you can modify the target. There are also more handlers, even some to modify the prototype getting, and setters for any property access yes!.
As you would imagine, this isn't supported in all browsers right now, but in Firefox you can play with the Proxy interface quite easy, just go to the MDN docs
It would make me happier if the managed to add some syntactic sugar on this, but anyway, its nice to have this kind of power in an already powerful language. Have a nice day! :)
PD: I didn't copy rosettacode js entry, I updated it.
There is a way to define a generic handler for calls on non-existant methods, but it is non-standard. Checkout the noSuchMethod for Firefox. Will let you route calls to undefined methods dynamically. Seems like v8 is also getting support for it.
To use it, define this method on any object:
var a = {};
a.__noSuchMethod__ = function(name, args) {
console.log("method %s does not exist", name);
};
a.doSomething(); // logs "method doSomething does not exist"
However, if you want a cross-browser method, then simple try-catch blocks if the way to go:
try {
a.doSomething();
}
catch(e) {
// do something
}
If you don't want to write try-catch throughout the code, then you could add a wrapper to the main object through which all function calls are routed.
function main() {
this.call = function(name, args) {
if(this[name] && typeof this[name] == 'function') {
this[name].call(args);
}
else {
// handle non-existant method
}
},
this.a = function() {
alert("a");
}
}
var object = new main();
object.call('a') // alerts "a"
object.call('garbage') // goes into error-handling code
It seems that you know your way around JS.
Unfortunately, I don't know of such feature in the language, and am pretty sure that it does not exist. Your best option, in my opinion is either using a uniform interface and extend it, or extend the prototypes from which your objects inherit (then you can use instanceof before going forward with the method call) or use the somewhat cumbersome '&&' operator in order to avoid the access of nonexistent properties/methods:
obj.methodName && obj.methodName(art1,arg2,...);
You can also extend the Object prototype with Anurag's suggestion ('call').
You can also check if the method exists.
if(a['your_method_that_doesnt_exist']===undefined){
//method doesn't exist
}