JavaScript Getter for All Properties

JavaScript getter for all properties

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"

JS Define getter for every property of a class

So if you run the code above you'll find it's not actually working. The console.log(hodor.holds) is going directly to the underlying this.holds instance property.

A quick search on StackOverflow led me to this Is it possible to implement dynamic getters/setters in JavaScript?. I've modified your code to use this Proxy approach and this now works correctly:

class Holder {
constructor(def = 'the door') {
this.holds = def;

return new Proxy(this, {
get(target, name, receiver) {
if (name in target) {
return target[name];
} else {
return `No property found named '${name}'`;
}
},
set(target, name, receiver) {
if (name in target) {
target[name] = receiver;
target.changed = true;
}
},
});
}
}

const hodor = new Holder();
console.log(hodor.holds); // Logs: the door
console.log(hodor.derp); // Logs: "No property found named 'derp'"
hodor.holds = 'bar';
console.log(hodor.holds); // Logs "bar"

javascript get set for all properties in an object

You need to change

var propertyName = property.toString();

to

let propertyName = property.toString();

otherwise whenever you update propertyName it will change that for all properties since the scope created by var allows for a single reference only (functional scope) whereas if you create it with let, each loop step will have its own reference (block scope).

var superhero = {        id: 0,        firstname: 'Duck',        lastname: 'Darkwing'    };        for (property in superhero)    {        let propertyName = property.toString();            // Store initial value in '_var' before overwriting it with get/set        this['_' + propertyName] = superhero[property];            Object.defineProperty(superhero, propertyName, {            set: function(value) {                this['_' + propertyName] = value;            },            get: function() {                return this['_' + propertyName];            }        });    }    console.log('--- Initial Value of test ---');    console.log(superhero);    console.log('-----------------------------');        superhero.firstname = 'Kal';    superhero.lastname = 'El';        console.log (superhero.firstname + ' ' + superhero.lastname);

javascript getter and setter within object property within class

I'm trying to do is get that same functionality, but where the property is not loose on the top level of the class, but within an object property inside the class.

Then you need to create that object somewhere first. As you will want to have a separate object (with different property values) for each instance, create that instance-specific object in the constructor:

class B {
#innerObjProp;
constructor() {
this.#innerObjProp = {};
}
}

and then define your getters/setters in that object, not on the class level, with the usual ES5 syntax for getters/setters in object literals:

class B {
#innerObjProp;
constructor() {
let b = "b"; // a local variable as a place to store the value
this.#innerObjProp = {
get b() {
// do some stuff that b depends on...
// ...
// ... and then ultimately return it:
return b;
},
set b(arg) {
// do some stuff that depends on b or vice versa...
// ...
// ... and then ultimately set b to something:
b = something();
}
};
}
}

Notice that this is a code smell. If your class has grown so large that you don't want to put some things at its "top level", something is wrong with your design, and you should consider splitting the class into multiple smaller classes.

Monitor All JavaScript Object Properties (magic getters and setters)

Looking through the nowjs source code, I believe they do this by continuously monitoring the now object and pushing changes between client and server whenever they are detected. I admit I haven't fully grokked their code yet, however.

In a browser, this would be done with some fun setInterval hacks.

EDIT: yes, that is indeed what they do: line 368 of the client now.js. They do some more tricks so that once a new property is detected, future access to it is caught by getters and setters, but those modifications are only made every 1000 ms in a setTimeout.

Another piece of evidence that this is impossible in current JavaScript is that the proxies proposal for ECMAScript Harmony is designed explicitly to enable such scenarios, implying very strongly that they can't be done currently. Recent Mozilla browsers have a prototype proxies implementation, if perhaps that's enough. And apparently V8 is working to add support, which could be enough depending on what version of V8 Node is using these days.

EDIT2: oh cool, on the server side apparently nowjs does use proxies! Which likely means they are mature enough in Node for your usage. See what they do at https://github.com/Flotype/now/blob/master/lib/proxy.js. Or just do var Proxy = require("nodejs-proxy") and hope they follow the spec so you can take advantage of the documentation from MDC and elsewhere.

How can I list all the get properties from a give object?

You can iterate over the property descriptors of the prototype (or of the object and every one of its prototypes) and see if the descriptor has a get function.

const logGetters = obj => {
if (!obj) return;
for (const [key, desc] of Object.entries(Object.getOwnPropertyDescriptors(obj))) {
if (desc.get && key !== '__proto__') console.log(key); // exclude Object.prototype getter
}
logGetters(Object.getPrototypeOf(obj));
};

class Foo {
#x;
#y;
constructor() {
this.#x = 20;
this.#y = 10;
}

get a() {
return "baa";
}
get n() {
return 20;
}
}

const f = new Foo();
logGetters(f);

Object with nested getters?

You can do this by having each intermediate object being an object, and overriding the prototype toString method. Unfortunately, the SO override of console.log will display the whole object, rather than call the toString as the chrome console would - but if you look at the actual console rather than the SO console you'll see the output you expect.

You can also call toString() explicitly as needed.

function Foo(){
this.bar = new Bar()
}
Foo.prototype.toString = () => "foo";

function Bar(){
this.baz = "Foo_Bar_Baz"
}
Bar.prototype.toString = () => "FooBar";

function Apple(){
this.pie = "Hot and tasty apple pie!"
}
Apple.prototype.toString = () => "Apples";

const constants = {
foo: new Foo(),
apple: new Apple()
}

console.log(constants.foo) //-> 'foo'
console.log(constants.foo.bar) //-> 'FooBar'
console.log(constants.foo.bar.baz) //-> 'foo_bar_baz'
console.log(constants.apple) //-> 'Apples'
console.log(constants.apple.pie) //-> 'Hot and tasty apple pie!'


Related Topics



Leave a reply



Submit