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
Related
There is an object obj which has several methods:
const obj = {
a() {}
b() { this.a() }
c() { this.a(); }
}
And as you can see, internally the methods call each other. a is getting called from b.
But I can also call a from outside like obj.a().
I want to differentiate internal call from outside call.
More context around the requirement:
This object is exposed in JS library which is used by websites as 3rd party library. We want to limit the methods exposed publicly on this object and so we want to know which ones are being called directly by websites currently so that we don't accidentally break them.
One trivial solution is adding an extra flag parameter to all methods and pass that parameter as true when calling internally. When called externally, the parameter would stay undefined. But this requires patching all methods and their invocation points. Ideally, I need a solution which works without patching all methods.
That does not directly answer your question but shows a common way how it is done in other libraries.
One thing that is commonly done is to have a version scheme:
Don't introduce breaking changes in patch (1.0.x) or minor (1.x) versions and only do breaking changes in major versions.
Now if a function should not be used anymore in your opinion mark it in some way as deprecated.
A way how you could fo that is to have something like this:
const obj = {
// prefix the private function, or choose another way to make clear it is private
// or that it can't be called publicly
_private_A() {
},
a() {
console.warn("`a` is deprecated use … instead. This function will be removed in version x.x see http://docs. … for more details.")
// you could also implement some functionality that logs that to your server
this._private_A();
},
b() { this._private_A(); },
c() { this._private_A(); }
}
Ideally, you would create some helper function for that so that you won't need to repeat yourself over and over again.
A module that was (I'm not sure if it is) commonly used was depd.
depd ensures that the logging is only done one the first call of the function, which prevents pollution of the logs.
But you don't need to use a module and you can easily reimplement its functionality yourself.
This gives you the flexibility to deprecate everything that might be problematic in your API, and gives the one using your API the feedback needed to update their code.
Doing a deprecation that way also allows you figure out if there are places in your code that still uses that deprecated function. And can make code/API rewrite easier.
Proxy is another way to target that problem but that requires the code where it runs on to support proxies which could be a problem (see https://caniuse.com/?search=proxy)
You would wrap object in a Proxy and provide that to the public:
const obj = {
a() { console.log('a')},
b() { this.a(); },
c() { this.a(); }
}
const handler = {
get: function (target, prop, receiver) {
console.log(prop+' was requested')
// do some checks
// do the original call.
return Reflect.get(...arguments);
},
};
const proxy2 = new Proxy(obj, handler);
proxy2.a()
Redesign your object like this:
class A {
publicMethod() {
this._privateWorker()
}
_privateMethod() {
this._privateWorker()
}
_privateWorker() {
...
}
}
and make it a rule that private (underscore) methods are only allowed to call other private methods, never a public one.
To answer the question as asked, you can inspect the stack and treat the call as internal if the stack starts with your class name:
class MyClass {
a() {
let stack = (new Error()).stack.split('\n').slice(2)
if (stack[0].includes('MyClass.'))
console.log('internal call')
else
console.log('external call')
}
b() {
this.a()
}
c() {
this.a();
}
}
obj = new MyClass;
obj.c();
obj.a();
I'm currently in the process of converting our Backbone application to ES6 syntax like e.g. this:
action: function(e){},
Becomes
action(e) {}
However, now I'm having this code:
throttleEvent: _.throttle(function(e) {
//do stuff
}, 500);
}
And I can't seem to find how to convert this to valid syntax.
I tried
throttleEvent _.throttle((e) => {
//do stuff
}, 500);
}
And
throttleEvent() {
return _.throttle((e) => {
//do stuff
}, 500);
}
But these all failed to work.
Help converting this to the valid syntax would be appreciated.
Well I'm not quite sure if the short syntax is applicable for your example.
Lets have a look at your start
action: function(e){},
you have an object, that object has a property called "action" and that property holds a function that later then can be called by obj.action().
Now to your example
throttleEvent: _.throttle(function(e) {}
Again you have an object, that has a property called throttleEvent. But the main difference is the value. The value is the return Value of the function _.throttle().
Taking from the documentation (https://lodash.com/docs/4.17.4#throttle)
Creates a throttled function that only invokes func at most once per every wait milliseconds
So your property actually holds the function that you got returned from the library. That explains why your second approach does not work. Because in this version every time you call the object property, you create a new throttle function.
(And to your first solution I think this is invalid syntax)
I think the best approach if you really want to use the short syntax is, assigning the throttle function to a variable before and then use it
For example something like this
const throttleFunction = _.throttle((event) => {
//do stuff
}, 500);
const obj = {
throttleEvent(event): any {
return throttleFunction(event);
},
};
But then it is to decide whether the ES6 syntax makes sense in this case or if you just stick with your original version
throttleEvent: _.throttle(function(e) {
//do stuff
}, 500)
Just because there is a different syntax available, does not mean you always have to use the new one. In some cases the "old one" makes actually more sense
try this syntax:
_.throttle(() => //Do stuff, 5000)
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.
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
}