What I was trying to accomplish. I wanted to share a single canvas (because what I'm doing is very heavy) and so I thought I'd make a limited resource manager. You'd ask it for the resource via promise, in this case a Canvas2DRenderingContext. It would wrap the context in a revokable proxy. When you're finished you are required to call release which both returns the canvas to the limited resource manager so it can give it to someone else AND it revokes the proxy so the user can't accidentally use the resource again.
Except when I make a proxy of a Canvas2DRenderingContext it fails.
const ctx = document.createElement('canvas').getContext('2d');
const proxy = new Proxy(ctx, {});
// try to change the width of the canvas via the proxy
test(() => { proxy.canvas.width = 100; }); // ERROR
// try to translate the origin of via the proxy
test(() => { proxy.translate(1, 2); }); // ERROR
function test(fn) {
try {
fn();
} catch (e) {
console.log("FAILED:", e, fn);
}
}
The code above generates Uncaught TypeError: Illegal invocation in Chrome and TypeError: 'get canvas' called on an object that does not implement interface CanvasRenderingContext2D. in Firefox
Is that an expected limitation of Proxy or is it a bug?
note: of course there are other solutions. I can remove the proxy and just not worry about it. I can also wrap the canvas in some JavaScript object that just exposes the functions I need and proxy that. I'm just more curious if this is supposed to work or not. This Mozilla blog post kind of indirectly suggests it's supposed to be possbile since it actually mentions using a proxy with an HTMLElement if only to point out it would certainly fail if you called someElement.appendChild(proxiedElement) but given the simple code above I'd expect it's actually not possible to meanfully wrap any DOM elements or other native objects.
Below is proof that Proxies work with plain JS objects. They work with class based (as in the functions are on the prototype chain). And they don't work with native objects.
const img = document.createElement('img')
const proxy = new Proxy(img, {});
console.log(proxy.src);
Also fails with the same error. where as they don't with JavaScript objects
function testNoOpProxy(obj, msg) {
log(msg, '------');
const proxy = new Proxy(obj, {});
check("get property:", () => proxy.width);
check("set property:", () => proxy.width = 456);
check("get property:", () => proxy.width);
check("call fn on object:", () => proxy.getContext('2d'));
}
function check(msg, fn) {
let success = true;
let r;
try {
r = fn();
} catch (e) {
success = false;
}
log(' ', success ? "pass" : "FAIL", msg, r, fn);
}
const test = {
width: 123,
getContext: function() {
return "test";
},
};
class Test {
constructor() {
this.width = 123;
}
getContext() {
return `Test width = ${this.width}`;
}
}
const testInst = new Test();
const canvas = document.createElement('canvas');
testNoOpProxy(test, 'plain object');
testNoOpProxy(testInst, 'class object');
testNoOpProxy(canvas, 'native object');
function log(...args) {
const elem = document.createElement('pre');
elem.textContent = [...args].join(' ');
document.body.appendChild(elem);
}
pre { margin: 0; }
Well FWIW the solution I choose was to wrap the canvas in a small class that does the thing I was using it for. Advantage is it's easier to test (since I can pass in a mock) and I can proxy that object no problem. Still, I'd like to know
Why doesn't Proxy work for native object?
Do any of the reasons Proxy doesn't work with native objects apply to situations with JavaScript objects?
Is it possible to get Proxy to work with native objects.
const handlers = {
get: (target, key) => key in target ? target[key] : undefined,
set: (target, key, value) => {
if (key in target) {
target[key] = value;
}
return value;
}
};
const { revoke, proxy } = Proxy.revocable(ctx, handlers);
// elsewhere
try {
proxy.canvas.width = 500;
} catch (e) { console.log("Access has been revoked", e); }
Something like that should do what you're expecting.
A revocable proxy, with handlers for get and set traps, for the context.
Just keep in mind that when an instance of Proxy.revocable() is revoked, any subsequent access of that proxy will throw, and thus everything now needs to use try/catch, in the case that it has, indeed, been revoked.
Just for fun, here's how you can do the exact same thing without fear of throwing (in terms of simply using the accessor; no guarantee for doing something wrong while you still have access):
const RevocableAccess = (item, revoked = false) => ({
access: f => revoked ? undefined : f(item),
revoke: () => { revoked = true; }
});
const { revoke, access: useContext } = RevocableAccess(ctx);
useContext(ctx => ctx.canvas.width = 500);
revoke();
useContext(ctx => ctx.canvas.width = 200); // never fires
Edit
As pointed out in the comments below, I completely neglected to test for the method calls on the host object, which, it turns out, are all protected. This comes down to weirdness in the host objects, which get to play by their own rules.
With a proxy as above, proxy.drawImage.apply(ctx, args) would work just fine.
This, however, is counter-intuitive.
Cases that I'm assuming fail here, are Canvas, Image, Audio, Video, Promise (for instance based methods) and the like. I haven't conferred with the spec on this part of Proxies, and whether this is a property-descriptor thing, or a host-bindings thing, but I'm going to assume that it's the latter, if not both.
That said, you should be able to override it with the following change:
const { proxy, revoke } = Proxy.revocable(ctx, {
get(object, key) {
if (!(key in object)) {
return undefined;
}
const value = object[key];
return typeof value === "function"
? (...args) => value.apply(object, args)
: value;
}
});
Here, I am still "getting" the method off of the original object, to call it.
It just so happens that in the case of the value being a function, I call bind to return a function that maintains the this relationship to the original context. Proxies usually handle this common JS issue.
...this causes its own security concern; someone could cache the value out, now, and have permanent access to, say, drawImage, by saying
const draw = proxy.drawImage;...
Then again, they already had the ability to save the real render context, just by saying
const ctx = proxy.canvas.getContext("2d");
...so I'm assuming some level of good-faith, here.
For a more secure solution, there are other fixes, though with canvas, unless it's in-memory only, the context is ultimately going to be available to anyone who can read the DOM.
Related
Let's say I have the following object with two functions as properties:
const foo = {
f1: () => {...},
f2: () => {...},
}
I would like to perform a specific action (for example, throw a custom error) when someone tries to execute a function that doesn't exist on the foo object.
I've tried using a get proxy, but that throws an error even when I'm not trying to execute f3, such as in the following code:
if (foo.f3) {...}
So how can I write my proxy in such a way that foo.f3 returns undefined as it usually would, but foo.f3() does throw an error?
Here's a partial solution, inspired by Unmiss.
const handler = {
get: function(obj, prop) {
if (prop in obj) {
return obj[prop];
} else {
return () => {
throw new Error(`Foo.${prop} is undefined`);
}
}
}
};
The problem with this is that while it accomplishes the goal of only throwing an error when you actually try to execute Foo.f3(), since Foo.f3 is now equal to that anonymous function is doesn't return undefined anymore, meaning that (as far as I can tell) if (Foo.f3) {...} will always return true.
Edit: as #paulpro points out:
You absolutely cannot do that. foo.f3 is either undefined or some
callable with custom logic; it cannot be both.
The best we could do is trap f3 in foo statements using the has trap, but this would mean if (f3 in foo) and if (foo.f3) would now have different results, which seems like a big red flag.
Is this what your asking for?
https://jsfiddle.net/MasterJames/bhesz1p7/23/
[obviously you need to F12 your dev tools to see the console output or change as desired]
Only real difference is to return undefined after throwing. It's as if the function executed without doing anything since it doesn't exist.
I'm sure there's a different solution based on the actual use case, but I like the idea/question. Keeps things more stable etc.
let foo = new Proxy(
{
f1: function (val) {
console.log(' F1 value:' + val);
return 'called OKAY with', val;
}
},
{
get: function(obj, prop) {
console.log("obj:", obj, " prop:", prop);
if (prop in obj) {
console.log("Found:", prop);
return obj[prop];
}
else {
console.log("Did NOT find:", prop);
throw new Error(`Foo.${prop} is undefined not called returning undefined`);
return undefined;
}
}
});
console.log("\nFoo Tester started");
console.log(' Does F1 exists', foo.f1 !== undefined);
console.log(' called F1 result:', foo.f1('passed') );
try {
console.log(' Does F2 exists', foo.f2 !== undefined);
console.log(' called F2 result:', foo.f2('passed') );
}
catch (err) {
console.log(' Error calling F2:', err );
}
console.log("Foo Tester finished");
Not sure you want to try-catch or not that's also up to you so in the end checking if it's real and a function is the same difference depending on how your going to handle the error.
if (foo.f2 && foo.f2.constructor === Function && foo.f2()) console.log("okay!");
Again you call build a safeCall wrapper more like this or something in between?
possible calling foo's 'customThrow' if it exists or what-have-you, so many possibilities with JS.
Okay so it took me sometime but I have a solution now.
I was not fully understanding your question, which I reformulated as a question within the question for myself to understand the issue better as it is complicated.
Basically you want to know if it's being called or not so the function you need in the proxies 'get' is 'isCalling'.
The solution is not clean in JS Fiddle because it's messy there at least for this kind of problem's solution.
Basically the solution is a sentence is, "you have to use an error to get a stack trace then retrace the source code that is calling and look for a right bracket or not.", to determine how it's being called and return whatever you want then).
[Please note this depends on your code and how you call it so you would adjust as needed.]
Since you have to find the location in the source code that's being called from it's way better if there is no inline script tag as is the case in this JSFiddle example. I'm using outerHTML to get the source, when arguments.callee.caller.toString() is better from an actual JS file. You'll also not the location from the stacktrace is skewed by odd behavior here, so with a normal JS file the code would align properly using other solutions are recommended. If anyone knows how to get a clean source that aligns with the error trace every time with script-tag blocks etc. Also note coming but not existing yet are things like Error.lineNumber.
[Please don't bother with the version history it was a nightmare to sort this one out. And again you would be better to use other npm packages to do the source code from stack trace parts.]
Anyway the example I believe achieves what you want but in principle demonstrates what you'd need to do better in a given real (no Fiddle) situation. I'm pretty sure doing this is not a great solution in production either and I've not tested the timing (performance speed) but if it really was that important to your cause (and no other better solution which I doubt) then it will work.
Originally I discovered this technique when I was doing something experimental, and instead of just sending another argument I was checking to see what was actually calling it and adjusting the functions action depending.
Usages are extensive when you start to think more about it as I did last year when I first did something like this. Examples are as an extra function execution Security Check, Realtime mystery-bug Debug Solution, a way to execute the function differently without passing more arguments, runaway recursive loops (how long is the stack), to name a few.
https://jsfiddle.net/MasterJames/bhesz1p7/90/
let foo = new Proxy(
{
f1: function (val) {
console.log(' F1 value:' + val);
return 'called OKAY with', val;
}
},
{
isCalling: function() {
let stk = new Error();
let sFrms = this.stkFrms(stk.stack);
console.log("stkFrms:", sFrms);
//BETTER From real pure JS Source
//let srcCod = arguments.callee.caller.toString()
let srcCod = document.getElementsByTagName('html')[0].outerHTML.split("\n");
let cItm = sFrms[(sFrms.length - 1)];
if(cItm !== undefined) {
let cRow = (parseInt(cItm[1]) - 3);
let cCol = (parseInt(cItm[2]) + 1);
let cLine = srcCod[cRow];
let cCod = cLine.substr(cCol, 1);
if(cCod === '(') return true;
}
return false;
},
stkFrms: function (stk) {
let frmRegex1 = /^.*at.*\(.*\:([0-9]*)\:([0-9]*)\)$/;
let frmRegex2 = new RegExp(frmRegex1.source, 'gm');
let res = [], prc, mtch, frms = stk.match(frmRegex2);
for(mtch of frms) {
prc = frmRegex1.exec(mtch);
res.push(prc);
}
return res;
},
get: function(obj, prop) {
if (prop in obj) {
console.log("Found:", prop);
return obj[prop];
}
else {
if(this.isCalling() === false) {
console.log("Did NOT find:", prop);
return undefined;
}
else {
console.log("Did NOT find return custom throw function:", prop);
return function() {throw new Error(`Foo.${prop} is undefined`);}
}
}
}
});
console.log("foo.f1:", foo.f1);
console.log("foo.f1('passed'):", foo.f1('passed'));
console.log("foo.f2:", foo.f2);
try {
console.log("foo.f2('passed2'):", foo.f2('passed2'));
}
catch(err) {
console.log("foo.f2('passed2') FAILED:", err);
}
console.log("'f2' in foo:", 'f2' in foo);
Okay so a verbal run through:
You want to check foo.f2 is undefined so it returns that because it's not being called.
If you do call it (f2) without simply checking first and erroring as needed, and you don't want to try-catch to throw your custom error based on the function name, you want it to return an actual function that will throw a custom error.
You also want to use 'in' to see that it's undefined, which is the same as false (maybe hack it further to send false instead of undefined via something like isCallingFromIn too.
Did I miss anything? Is this not what you all thought was impossible?
Background
We have a request object that contains information. That specific object has a field called partnerId which determines what we are going to do with the request.
A typical approach would be a gigantic if/then/else:
function processRequest( request ){
if( request.partnerId === 1 ){
//code here
}else if( request.partnerId === 23 ){
//code here
}
//and so on. This would be a **huge** if then else.
}
This approach has two main problems:
This function would be huge. Huge functions are a code smell (explaining why next) but mainly they become very hard to read and maintain very quickly.
This function would do more than one thing. This is a problem. Good coding practices recommend that 1 function should do only 1 thing.
Our solution
To bypass the previous problems, I challenged my co-worker to come up with a different solution, and he came up with a function that dynamically builds the name of the function we want to use and calls it. Sounds complicated but this code will clarify it:
const functionHolder = {
const p1 = request => {
//deals with request
};
const p23 = request => {
//deals with request
};
return { p1, p23 };
};
const processRequest = request => {
const partnerId = request.partnerId;
const result = functionHolder[`p${partnerId}`](request);
return result;
};
Problems
This solution has advantages over the previous one:
There is no main function with an huge gigantic if then else.
Each execution path is not a single function that does one thing only
However it also has a few problems:
We are using an object functionHolder which is in reality useless. p1 and p23 don't share anything in common, we just use this object because we don't know how else we can build the function's name dynamically and call it.
There is no else case. If we get an incorrect parameter the code blows.
Out eslint with rule non-used-vars complains that p1 and p23 are not being used and we don't know how to fix it ( https://eslint.org/docs/rules/no-unused-vars ).
The last problem, gives us the impression that perhaps this solution is not so great. Perhaps this pattern to avoid an if then else has some evil to it that we are yet to find.
Questions
Is there any other pattern we can use to avoid huge if then else statements ( or switch cases )?
Is there a way to get rid of the functionHolder object?
Should we change the pattern or fix the rule?
Looking forward to any feedback!
You can get rid of the unused variables by never declaring them in the first place:
const functionHolder = {
p1: request => {
//deals with request
},
p23: request => {
//deals with request
};
};
const processRequest = request => {
const partnerId = request.partnerId;
const method = functionHolder[`p${partnerId}`]
if(method) // Is there a method for `partnerId`?
return method(request);
return null; // No method found. Return `null` or call your default handler here.
};
To answer your points:
Yeap, as shown above.
Not without some kind of object.
That's up to you. Whatever you prefer.
Perhaps I'm not understanding the question properly, but why not an object to hold the methods?
const functionHolder = {
1: function(request) {
// do something
},
23: function(request) {
// do something
},
_default: function(request) {
// do something
}
}
function processRequest(request) {
(functionHolder[request.partnerId] || functionHolder._default)(request)
}
Explanation:
The object functionHolder contains each of the methods used to deal with a given request.
The keys of functionHolder (e.g. 1) correspond directly to the values of request.partnerId, and the values of these members are the appropriate methods.
The function processRequest "selects" the appropriate method in functionHolder (i.e. object[key]), and calls this method with the request as the parameter (i.e. method(parameter)).
We also have a default method, under the key _default, if request.partnerId does not match any existing key. Given a || b; if a is "falsy", in this case undefined (because there is no corresponding member of the object), evaluate to b.
If you are concerned about making functionHolder "bloated", then you can separate each of the methods:
const p1 = request => {
// do something
}
const p23 = request => {
// do something
}
const _default = request => {
// do something
}
And then combine them into a "summary" object of sorts.
const functionHolder = {
1: p1,
23: p23,
_default: _default
}
processRequest remains the same as above.
This adds a lot of global variables though.
Another advantage is you can import / change / declare methods on the fly. e.g.
functionHolder[1] = p1b // where p1b is another request handler (function) for `request.partnerId` = 1
functionHolder[5] = p5 // where p5 is a request handler (function) that has not yet been declared for `request.partnerId` = 5
Combining the above, without having to declare many global variables while also being able to separate the declaration of each method:
const functionHolder = {}
functionHolder._default = request => {
// do something
}
functionHolder[1] = request => {
// do something
}
functionHolder[23] = request => {
// do something
}
processRequest remains the same as above.
You just have to be sure that the methods are "loaded in" to functionHolder before you call processRequest.
I am trying to make a Proxy object of Image to trap properties but even with an empty handler I get an error message.
TypeError: Argument 1 of Node.appendChild does not implement interface Node.
The proxy object is suppose to act as the target object so this baffles me a little. As far as I understand you should be able to do this with DOM nodes as well (?).
Also: I cannot start loading the image and have the onload handler triggered when setting the src property.
How should I use the Proxy so I can "take over" for example the "src" property and otherwise have it act like a regular image object?
My code
'use strict';
//--- normal image use ---
var imgNormal = new Image();
imgNormal.onload = function(){
console.log('Normal loaded OK');
document.body.appendChild(imgNormal);
};
imgNormal.src = 'https://i.imgur.com/zn7O7QWb.jpg';
//--- proxy image ---
var imgProxy = new Proxy(Image, { // I also tried with 'new Image()' and HTMLImageElement
set: function(a,b,c,d){
console.log('set '+b);
return Reflect.set(a,b,c,d);
}
});
imgProxy.onload = function(){
console.log('Proxy loaded OK');
document.body.appendChild(imgProxy);
};
imgProxy.src = 'https://i.imgur.com/zn7O7QWb.jpg';
document.body.appendChild(imgProxy); // double-up to demo error
Update: Thanks to #Harangue! using "new" (bah..) certainly made the proxy object come to life but now I am unable to trap the setting of properties. It seem to ignore the trap completely - example:
var proxy = new Proxy(Image, {
set: function(a,b,c,d){
console.log('set '+b); // doesn't show
return Reflect.set(a,b,c,d);
}
});
var imgProxy = new proxy();
imgProxy.onload = function(){
console.log('Proxy loaded OK');
document.body.appendChild(imgProxy);
};
imgProxy.src = 'https://i.imgur.com/zn7O7QWb.jpg';
How can I trap the property setting using a valid proxy?
Update 2 On the other hand - using new with the new proxy only seem to use the original constructor. All examples I can find does not use new:
var myProxy = new Proxy(.., ..); // should suffer
Using then on top of that new myProxy() only seem to use the original constructor which is not what I want as it ignores the traps.
var proxy = new Proxy(Image, {}); //should be sufficent??
var proxy2 = new proxy();
console.log(proxy2); //-> says Image (not proxy..)
The traps seem to work in my first attempts but the proxy doesn't behave as expected. This is so confusing, and so new. Happy for any input how both of these can be solved (traps and behavior).
Never underestimate the importance of the new keyword. ;)
//--- proxy image ---
var imgProxy = new Proxy(Image, { // I also tried with 'new Image()'
set: function(a,b,c,d){
console.log('set '+b);
return Reflect.set(a,b,c,d);
}
});
imgProxy.src = 'https://i.imgur.com/zn7O7QWb.jpg';
document.body.appendChild(new imgProxy); // double-up to demo error
With the proxy you effectively extend the Image object. But sending the Image constructor itself, rather than the DOM Node returned by it, would indeed be missing the needed appendChild.
As an alternative to a proxy you can also overwrite the property on the object itself and therefore control it's behaviour:
function findDescriptor(obj, prop){
if(obj != null){
return Object.hasOwnProperty.call(obj, prop)?
Object.getOwnPropertyDescriptor(obj, prop):
findDescriptor(Object.getPrototypeOf(obj), prop);
}
}
var img = new Image();
var {get, set} = findDescriptor(img, "src");
Object.defineProperty(img, "src", {
configurable: true,
enumerable: true,
//get: get, //keep behaviour
get(){ //overwrite getter
var v = get.call(this); //call the original getter
console.log("get src:", v, this);
return v;
},
//same for setter
set(v){
console.log("set src:", v, this);
//modify value before applying it to the default setter
v = v.toLowerCase();
set.call(this, v);
}
});
img.src = "FileWithUppercaseLetters.jpg"; //setter
img.src; //trigger getter
And since this property is defined on the Image.prototype*, you can simply extend this class and modify the behaviour on the prototype of the inherited Class
*at least in FF, have to check the other browsers
So if you open up the inspector, you get this (if you're unlucky):
I'm building a tiny JS component which displays debugging information - is there any way to read the number of encountered errors and warnings so far?
A hacky solution I could come up with involves a bit of trickery by replacing the console.(error|log|warn) functions with my own, but I'm yet to test if it works for all cases (e.g. outside of code I own).
Is there a better way to do this?
As noted in this answer, it's generally not a good idea to change the behavior of native objects/methods. However, the following code should get you what you need in a fairly innocuous manner:
// Add this IIFE to your codebase:
(() => {
// Get all of the property names of the console:
const methodsToTrack = Object.keys(window.console);
// Create an object to collect total usage tallies in:
const usageRegistry = {};
for (let i = 0, j = methodsToTrack.length; i < j; i++) {
let methodName = methodsToTrack[i];
// If the property is not a method, don't touch it:
if(typeof window.console[methodName] !== 'function') {
continue;
}
// Cache the original console method here:
let consoleMethod = window.console[methodName];
// Overwrite console's method to increment the counter:
window.console[methodName] = function () {
// Defining registry properties here, so the registry only contains values for methods that were accessed:
usageRegistry[methodName] = usageRegistry[methodName] || 0;
// Execute the original method's behavior, capturing the returned value (if any) in a var, to return it at the end:
const returnedValue = consoleMethod(...arguments);
// Increment the usage registry for the executed method:
usageRegistry[methodName]++;
// Return the value the console's method would have returned, so the new method has the same signature as the old.
return returnedValue;
};
}
// Define a funciton to output the totals to a console log, then clean up after itself:
window.showConsoleTallies = function () {
window.console.log(usageRegistry);
usageRegistry['log']--;
}
})();
// Examples:
showConsoleTallies();
console.log('log 1');
console.error('error 1');
console.log('log 2');
console.warn('warn 1');
console.error('error 2');
console.log('log 3');
showConsoleTallies();
PS: That's the ECMA6 version, but feel free to run it through Babel if you'd like it to be compiled for use in older browsers.
Long story short: I'm in a situation where I'd like a PHP-style getter, but in JavaScript.
My JavaScript is running in Firefox only, so Mozilla specific JS is OK by me.
The only way I can find to make a JS getter requires specifying its name, but I'd like to define a getter for all possible names. I'm not sure if this is possible, but I'd very much like to know.
Proxy can do it! I'm so happy this exists!! An answer is given here: Is there a javascript equivalent of python's __getattr__ method? . To rephrase in my own words:
var x = new Proxy({}, {
get(target, name) {
return "Its hilarious you think I have " + name
}
})
console.log(x.hair) // logs: "Its hilarious you think I have hair"
Proxy for the win! Check out the MDN docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Works in chrome, firefox, and node.js. Downsides: doesn't work in IE - freakin IE. Soon.
You can combine proxy and class to have a nice looking code like php:
class Magic {
constructor () {
return new Proxy(this, this);
}
get (target, prop) {
return this[prop] || 'MAGIC';
}
}
this binds to the handler, so you can use this instead of target.
Note: unlike PHP, proxy handles all prop access.
let magic = new Magic();
magic.foo = 'NOT MAGIC';
console.log(magic.foo); // NOT MAGIC
console.log(magic.bar); // MAGIC
You can check which browsers support proxy http://caniuse.com/#feat=proxy.
The closest you can find is __noSuchMethod__ (__noSuchMethod__ is deprecated), which is JavaScript's equivalent of PHP's __call().
Unfortunately, there's no equivalent of __get/__set, which is a shame, because with them we could have implemented __noSuchMethod__, but I don't yet see a way to implement properties (as in C#) using __noSuchMethod__.
var foo = {
__noSuchMethod__ : function(id, args) {
alert(id);
alert(args);
}
};
foo.bar(1, 2);
Javascript 1.5 does have getter/setter syntactic sugar. It's explained very well by John Resig here
It's not generic enough for web use, but certainly Firefox has them (also Rhino, if you ever want to use it on the server side).
If you really need an implementation that works, you could "cheat" your way arround by testing the second parameter against undefined, this also means you could use get to actually set parameter.
var foo = {
args: {},
__noSuchMethod__ : function(id, args) {
if(args === undefined) {
return this.args[id] === undefined ? this[id] : this.args[id]
}
if(this[id] === undefined) {
this.args[id] = args;
} else {
this[id] = args;
}
}
};
If you're looking for something like PHP's __get() function, I don't think Javascript provides any such construct.
The best I can think of doing is looping through the object's non-function members and then creating a corresponding "getXYZ()" function for each.
In dodgy pseudo-ish code:
for (o in this) {
if (this.hasOwnProperty(o)) {
this['get_' + o] = function() {
// return this.o -- but you'll need to create a closure to
// keep the correct reference to "o"
};
}
}
I ended up using a nickfs' answer to construct my own solution. My solution will automatically create get_{propname} and set_{propname} functions for all properties. It does check if the function already exists before adding them. This allows you to override the default get or set method with our own implementation without the risk of it getting overwritten.
for (o in this) {
if (this.hasOwnProperty(o)) {
var creategetter = (typeof this['get_' + o] !== 'function');
var createsetter = (typeof this['set_' + o] !== 'function');
(function () {
var propname = o;
if (creategetter) {
self['get_' + propname] = function () {
return self[propname];
};
}
if (createsetter) {
self['set_' + propname] = function (val) {
self[propname] = val;
};
}
})();
}
}
This is not exactly an answer to the original question, however this and this questions are closed and redirect here, so here I am. I hope I can help some other JS newbie that lands here as I did.
Coming from Python, what I was looking for was an equivalent of obj.__getattr__(key)and obj.__hasattr__(key) methods. What I ended up using is:
obj[key] for getattr and obj.hasOwnProperty(key) for hasattr (doc).
It is possible to get a similar result simply by wrapping the object in a getter function:
const getProp = (key) => {
const dictionary = {
firstName: 'John',
lastName: 'Doe',
age: 42,
DEFAULT: 'there is no prop like this'
}
return (typeof dictionary[key] === 'undefined' ? dictionary.DEFAULT : dictionary[key]);
}
console.log(getProp('age')) // 42
console.log(getProp('Hello World')) // 'there is no prop like this'