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
How to Write Asynchronous Functions for Node.Js
Passing Variable from JavaScript to Ruby on Rails
How to Detect When a Youtube Video Finishes Playing
How to Pass Data from JavaScript to Swift Within a Wkwebview
Sorting Arrays Numerically by Object Property Value
How to Import Variables in JavaScript (Node.Js)
Where Do the Parameters in a JavaScript Callback Function Come From
Get Word Under Tap from Uiwebview Using JavaScript
Dt: Link Binding Is Lost After Re-Rendering the Table
Embed a JavaScript Engine in an iOS Application
Passing Functions to Settimeout in a Loop: Always the Last Value
Switch on Ranges of Integers in JavaScript
Adding/Removing Items from a JavaScript Object with Jquery
Compare Two JavaScript Arrays and Remove Duplicates
What Are Asynchronous Functions in JavaScript? What Is "Async" and "Await" in JavaScript