I'm trying to understand what the advantage is, if any, of the following:
const obj = {
forename: 'Foo',
surname: 'Bar',
get name() {
//yes I get that I can do other stuff here
return this.forename+' '+this.surname;
}
}
alert(obj.name); //Foo Bar
...over...
const obj = {
forename: 'Foo',
surname: 'Bar',
name() {
return this.forename+' '+this.surname;
}
}
alert(obj.name()); //Foo Bar
I've read around (1; 2; 3) but can't seem to see any benefit beyond readability and code style. Is that all it is? No implicit behavioural changes between the two methods?
Is it with one eye on future class property/method visibility in JavaScript? That would make sense - getters for private properties. But since that's not here yet, I can't see the point of the above.
Can anyone enlighten me?
One difference is typeof will actually work as expected when using a getter, that is, it will return the actual type of the primitive returned by the getter, while using a method will always return function:
const objGetter = {
forename: 'Foo',
surname: 'Bar',
get name() {
return `${ this.forename } ${ this.surname }`;
}
}
const objGetterLogic = {
forename: undefined,
surname: 'Bar',
get name() {
return this.forename ? this.surname : 3;
}
}
const objMethod = {
forename: 'Foo',
surname: 'Bar',
name() {
return `${ this.forename } ${ this.surname }`;
}
}
console.log(`objGetter`, typeof objGetter.name);
console.log(`objMethod`, typeof objMethod.name);
console.log(`objGetterLogic (without forename)`, typeof objGetterLogic.name);
objGetterLogic.forename = 'Alice';
console.log(`objGetterLogic (with forename)`, typeof objGetterLogic.name);
Of course, you can call name() in the version with the method, but with the getter that will work transparently.
Also, if you have nested getters, you can call them transparently, which is something that comes in handy if you are navigating an object programmatically, as otherwise, you would need to account for the possibility of properties being either a value or a function that needs to be called to get the actual value you need:
class Shape {
constructor(type, children) {
this.type = type || '';
this.children = children || [];
}
get firstChild() {
return this.children[0];
}
get lastChild() {
return this.children[this.children.length - 1];
}
}
const group1 = new Shape('group1', [
new Shape('a'),
new Shape('b'),
new Shape('c'),
]);
const group2 = new Shape('group2', [
new Shape('d'),
new Shape('e'),
new Shape('f'),
]);
const group4 = new Shape('group4', [
group1,
group2,
]);
console.log(group4.firstChild.lastChild.type);
In any case, I think one of the greatest advantages of getters and setters are just increased readability and reduced verbosity, even though that usually comes down to personal preference anyway. In any case, I rather use:
person.name = 'Alice Smith';
Than:
person.setName('Alice', 'Smith');
But we could also argue the latter might be more appropriate in some cases.
Since you defined just a getter for name, without defining a setter, if you attempt to change its value, for example obj.name = "foo", nothing is going to happen.
On the other hand, if you try to change your name property on your second object, nothing will stop you by doing so.
Since the capabilities of these two features are identical, we need to consider the readability and writeability of code that interacts with such a property (or method).
The entire advantage is the ability to run functions when accessing a property. "Ah," you say, "but if I wanted to run a function, I'd just use a function." And you'd be correct -- as I say initially -- that the two have no difference in terms of capabilities. But accessing a property is different from calling a function for the person writing code that interacts with your object.
Sylistically, I expect accessor methods to have names that start with get. Your example function called name seems poor style to me, whereas a property called name seems much better. If you ask me what advantage obj.getName() has over obj.name I can readily tell you the latter is much shorter to type! There are corresponding readability concerns with setters: obj.setBirthday(new Date(...)) versus a setter property, obj.birthday = new Date(...) (suppose, e.g., that this mutates a corresponding age property)
That's it -- all languages are designed to make a Turing-complete set of functionality more comprehensible to human authors and readers. If a feature doesn't help you achieve that goal, don't use it!
As a syntactic sugar it can be useful to make your code if not more readable then at least less clogged: x = a().b().c().d() vs x = a.b.c.d
Suppose you already have a code like that obj.attr... a lot of code like that, and at some point you realize that you need a function instead of access operator in all these places Get/set to the rescue. Especially useful in debug to catch a property access. And even more so if you have to work with code you have no control over
Sometimes using getter is more idiomatic. Reader expects that obj.attr has no side effects, but he is not so sure if he sees a function
Related
Assume we have some classes A and B:
Class A {
constructor(a) {
this.a = a;
};
showInfo() {
console.log(this.a)
};
};
Class B {
constructor(b) {
this.b = b;
};
printText() {
console.log('its B!');
};
};
Then we create an instance of B like this:
const objB = new B(
new A(3)
);
So now we have objB with its own method inside - printText, and we surely can call it.
But what if i want somehow when calling not existing method in objB to make it pass through to encapsulated A class in there and look for invoking this method on him, like this: objB.showInfo() - to give me 3 here ?
Same story, but at this time i want when calling not existing method on A to make it pass through to B outside (like that printText)?
P.S. Don't wanna use super() and inheritance, just composition and wrapping objects, hope you've got the point.
Just a little warning at the start: this might make your program harder to debug, and it also might be a little complicated for something like this. As others have suggested, you should probably investigate other options which may be simpler and also less in the way of everything else your code does.
Here's the code which provides the functionality:
function makeGetProxy(t){
return new Proxy(t, {
get(obj,prop){
if(prop in t){
return t[prop];
}else{
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
var val = t[keys[i]];
if(prop in val){
return val[prop];
// what about a recursive function?
}
}
return undefined;
}
}
});
}
And one itty bitty change to your constructor in B:
class B {
constructor(b) {
this.b = b;
return makeGetProxy(this);
};
printText() {
console.log('its B!');
};
};
If you want, you can also do the same to A.
Let's slow down. What just happened? I'll explain.
Since the properties we might request don't already exist, we're going to have to use a getter (see resources) to properly send back the value required. But, since we don't know the property names, we need a Proxy (see resources) to have a "catch-all" kind of get method.
The proxy will check if the property requested prop already exists, and if so, returns it. If it doesn't exist, it checks all of your properties' properties (all of the sub-properties).
The first result it gets, it returns it. This might cause unexpected bugs in your program. If it doesn't find it, it simply returns undefined.
Then, the proxy is generalized into a function for reuse in multiple classes (as you requested).
So, this can get the properties of a class and the properties of a class' properties, but if you need to go further (with your C class that doesn't exist yet), you can use a recursive function. I currently don't have the implementation for that recursive function, but in my head it would comprise mostly of a modified version of the else block in the makeGetProxy function.
Again, be careful with this code. It might get in the way of other things and cause unnecessary difficulty in debugging.
Resources:
Getters (MDN)
Proxy (MDN)
I borrowed some code from this answer and got the Proxy idea from this answer.
I'm building a web framework, something like React; one of the things which I would like to improve on React is state.
My idea is something like Svelte, to use state you just create a normal variable (in my case it would be okay to use a function when creating te state, but not when updating it), but how Svelte does this Magic is by compiling, and I would like it to work in vanilla Javascript.
From my understanding this is not exactly possible, but I've still been trying to hack something somehow.
So the part of this state system that is not possible is knowing when a primitive is set and got (setters & getters), I want it to work with scoped variables; so I can't use the Object.defineProperty on the window or globalThis. I've been hacking around for quite some time and here are the only solutions I thought have could worked:
Proxing a new String(string), has given weird error of this beeing of the wrong type, unknows values, and stuff.
Proxing the Funtion.arguments object, but this didn't work.
Using Symbol.toPrimitive, but I couldn't find a way of using it without a + or ${}.
But as you can see they all have problems, I'm stuck and can't find anything, is there any (even if hacky, though without legacy or deprecated code) way to do this? Thank you!
You can't do what you've described in JavaScript. You can't proxy a primitive, and you can't run code some other way (getter, setter) when a variable is read or set, only when a property of an object is read or set.
There is an awful thing you can do in loose mode that's disallowed (for good reasons) in strict mode where you have an object with getters and setters that you then put into the environment used for resolving freestanding identifiers using the with statement, but again, it's disallowed for good reasons in strict mode (which is the default for modules and other mechanisms that create new contexts, like the body of a class).
I hesitate to give an example of it, but for completeness:
// This only works in loose mode, not strict mode
let a = 0;
const obj = {
get a() {
console.log(`Getter called, returning a = ${a}`);
return a;
},
set a(value) {
console.log(`Setter called, setting a = ${value}`);
a = value;
}
};
with (obj) {
console.log(a);
a = 42;
console.log(a);
}
Re your updated question:
My idea is something like Svelte, to use state you just create a normal variable...but how Svelte does this this Magic is by compiling, and I would like it to work in vanilla Javascript.
I wouldn't try to do it with freestanding variables, have the user provide a state object and convert its data properties to getter/setter combinations (or replace it with a new version with getter/setter combinations, etc.):
// Userland code provides a state object
const state = {
a: 0,
b: "hi",
};
// Your framework code converts it to using getters/setters
function enhance(obj) {
const descrs = Object.getOwnPropertyDescriptors(obj);
for (const key of Object.keys(descrs)) {
const descr = descrs[key];
if (descr.configurable && "value" in descr && typeof descr.value !== "function") {
// A simple data property; wrap it in getter/setter
let value = descr.value;
if (typeof value === "object") {
enhance(value);
} else {
Object.defineProperty(obj, key, {
get() {
console.log(`Getter called, returning ${key} = ${value}`);
return value;
},
set(newValue) {
console.log(`Setter called, setting ${key} = ${newValue}`);
value = newValue;
},
enumerable: descr.enumerable,
configurable: true,
});
}
}
}
}
enhance(state);
// Their code using the properties triggers your getter/setters:
console.log(state.a, state.b);
state.a = 42;
state.b = state.b.toUpperCase();
console.log(state.a, state.b);
I'm reading some Typescript code and I have a hard time understanding a line.
const symbol = Symbol.for('symbolname');
class Something {
public get [symbol]() {
return true;
}
...
}
How does exactly get work? Is symbol an argument?
Symbol
According to Developer Mozilla
Every Symbol() call is guaranteed to return a unique Symbol. Every Symbol.for("key") call will always return the same Symbol for a given value of "key". When Symbol.for("key") is called, if a Symbol with the given key can be found in the global Symbol registry, that Symbol is returned. Otherwise, a new Symbol is created, added to the global Symbol registry under the given key, and returned.
Well, to make it simple, if you're familiar with object[key] in Javascript, you can understand Symbol easily.
Occasionally, we use key in some circumstances like below
const key = "keyname"
const object = {
[key]: "value"
}
Now we simply convert it to symbol usage
const symbol = Symbol.for('symbolname');
const object = {
[symbol]: "value" //the key is presented by a symbol
}
But beyond that, one outstanding feature, which we regularly use Symbol, is
Symbol-keyed properties will be completely ignored when using JSON.stringify():
It's really good to serialize JSON data but ignore some fields in your code
If you want to have a better understanding of some Symbol features, I'd suggest you read this article
Getter (in your code it's calling get)
According to Developer Mozilla again
The get syntax binds an object property to a function that will be called when that property is looked up.
But firstly, we ask why we need it?
To demonstrate it, let me show you one of my favorite examples
class Person {
public firstName: string;
public lastName: string;
public fullName: string; //you will assign a value for `fullName` like this `${firstName} ${lastName}`?
}
That fullName assignment will be repeated multiple times ridiculously even though you assigned values for firstName and lastName
To avoid that, we'd introduce getter
class Person {
public firstName: string;
public lastName: string;
//here we go with getter!
public get fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
After this, you only need to assign values for firstName and lastName. And for fullName, you just simply call person.fullName which will be populated automatically!
And the last question is "Are we able to use getter with Symbol". I'd say YES!
If you check the earlier document for the getter, you can find this part
const expr = 'foo';
const obj = {
get [expr]() { return 'bar'; }
};
console.log(obj.foo); // "bar"
Conclusion
About what you ask
const symbol = Symbol.for('symbolname');
class Something {
public get [symbol]() {
return true;
}
}
Well, I guess that developer is trying to avoid unexpected data population during JSON serialization on that getter
I'm working on a Codecademy course and have written the following code that works:
let person = {
_name: 'Lu Xun',
_age: 137,
set age (newAge) {
if (typeof newAge === 'number'){
this._age= newAge;
} else {
console.log('Invalid input');
}
}
}
person.age= 22;
console.log(person['_age']);
I was getting my head around how the set works and thought that it essentially worked the same as defining a method within the person object just with different syntax.
So I tried it out, called the age method passing 22 to it from outside the object using the normal way you would call a method. It worked the same. See below.
let person = {
_name: 'Lu Xun',
_age: 137,
age: function (newAge) {
if (typeof newAge === 'number'){
this._age= newAge;
} else {
console.log('Invalid input');
}
}
}
person.age(22);
console.log(person['_age']);
What are the benefits of using the 'set' syntax? ie why use it when a normal function/method definition works in the same way?
Imagine you wrote a very huge code and a lot of them access a variable like this:
// The object
const person = { age: 12 }
// You access it very often
console.log(person.age);
Now you certainly want to calculate the age based on the birthdate. With a method, you would have to replace person.age with person.getAge() everywere you use it. That can take time. With getters / setters you can just replace one single line and everything keeps working.
If you don't think that this is a problem, you might wanna look at this java thread. Java doesnt have getters / setters.
The get/set syntax is for a property whereas using a method is just that, a method. Most of the time a simple public variable will suffice however there are times, like in your example that you want to validate the input to ensure that it meets whatever expectations you have of the value.
Also consider how your code will be called and what that will look like. If you were to use a function to change the value and a function to get the value your code will look something like this:
var myObject = new CoolObject('some parameter');
myObject.setComment('this is a super cool comment');
console.log(myObject.getComment());
Where as if you implemented the get/set functions on your property it will look like this
var myObject = new CoolObject('some parameter');
myObject.comment = 'This is a super cool comment';
console.log(myObject.comment);
At the end of the day it really doesn't matter as both code snippets will do the exact same thing however one reads easier and wont get you yelled at by your co-workers. Keep in mind the second example doesn't change if you simply expose a public variable.
I'm working with XULRunner and came across the following pattern in a code sample:
var StrangeSample = {
backingStore : "",
get foo() { return this.backingStore + " "; },
set foo(val) { this.backingStore = val; },
func: function(someParam) { return this.foo + someParam; }
};
StrangeSample.foo = "rabbit";
alert(StrangeSample.func("bear"));
This results in "rabbit bear" being alerted.
I've never seen this get/set pattern used in Javascript before. It works, but I can't find any documentation/reference for it. Is this something peculiar to XUL, a recent language feature, or just something I missed? I'm puzzled because I was specifically looking for something like this a few months ago and couldn't find anything.
For reference, removing "get" or "set" results in a syntax error. Renaming them to anything else is a syntax error. They really do seem to be keywords.
Can anyone shed some light on this for me, or point me towards a reference?
As suggested by Martinho, here are some links explaining the getter/setters in JS 1.5:
http://ejohn.org/blog/javascript-getters-and-setters/
http://ajaxian.com/archives/getters-and-setters-in-javascript
Be aware though, they don't seem to be supported in IE, and some developers have (legitimate) concerns about the idea of variable assignment having side-effects.
get/set are not reserved keywords as Daniel points out. I had no problem creating a top-level functions called "get" and "set" and using the alongside the code-sample posted above. So I assume that the parser is smart enough to allow this. In fact, even the following seems to be legitimate (if confusing):
var Sample = {
bs : "",
get get() { return this.bs; },
set get(val) { this.bs = val; }
}
According to Mozilla, they are not in ECMAScript.
JavaScript Setters And Getters:
Usually the setter and getter methods follow the following syntax in JavaScript objects. An object is created with multiple properties. The setter method has one argument, while the getter method has no arguments. Both are functions.
For a given property that is already created within the object, the set method is typically an if/else statement that validates the input for any time that property is directly accessed and assigned later on via code, a.k.a. "set". This is often done by using an if (typeof [arg] === 'certain type of value, such as: number, string, or boolean') statement, then the code block usually assigns the this.(specific)property-name to the argument. (Occasionally with a message logging to the console.) But it doesn't need to return anything; it simply is setting the this.specific-property to evaluate to the argument. The else statement, however, almost always has a (error) message log to the console that prompts the user to enter a different value for the property's key-value that meets the if condition.
The getter method is the opposite, basically. It sets up a function, without any arguments, to "get", i.e. return a(nother) value/property when you call the specific-property that you just set. It "gets" you something different than what you would normally get in response to calling that object property.
The value of setters and getters can be easily seen for property key-values that you don't want to be able to be directly modified, unless certain conditions are met. For properties of this type, use the underscore to proceed the property name, and use a getter to allow you to be able to call the property without the underscore. Then use a setter to define the conditions by which the property's key-value can be accessed and assigned, a.k.a. "set". For example, I will include two basic setters and getters for this object's properties. Note: I use a constant variable because objects remain mutable (after creation).
const person = {
_name: 'Sean';
_age: 27;
set age(ageIn) {
if (typeof ageIn === 'number') {
this._age = ageIn;
}
else {
console.log(`${ageIn} is invalid for the age's key-value. Change ${ageIn} to/into a Number.`);
return 'Invalid Input.';
}
},
get age() {
return this._age;
},
set name(nameIn) {
if (typeof nameIn === 'string') {
this._name = nameIn;
} else {
console.log(`Change ${nameIn} to/into a(ny) String for the name's
key-value.`);
return 'Invalid Input.';
}
},
get name() {
return this._name;
}
};
Where it gets interesting is when you try to set/assign a new key-value for the _age property, because it has to meet the if conditional in order to be successfully assigned, meaning not all assignments are valid, etc.
person.age = 'twenty-eight'; /* output: twenty-eight is invalid for the
age's key-value. Change twenty-eight to/into a Number. */
console.log(person.age); // output: 27 (twenty-eight was never assigned)
person.age = 28; // output: none
console.log(person.age); // output: 28
Note how I was able to access the person._age property via the person.age property thanks to the getter method. Also, similar to how input for age was restricted to numbers, input for the name property is now restricted/set to strings only.
Hope this helps clear things up!
Additionally, some links for more:
https://johnresig.com/blog/javascript-getters-and-setters/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
https://www.infragistics.com/community/blogs/infragistics/archive/2017/09/19/easy-javascript-part-8-what-are-getters-and-setters.aspx