When is a getter function useful in JavaScript? - javascript

Specifically when used within objects, when would a getter function be used over a regular function. For example, what is the difference between using
const obj = {
get method() {
return data;
}
};
console.log(obj.method);
and this
conts obj = {
method() {
return data;
}
};
console.log(obj.method());

1 . With a normal property/method, you can change its value.
A getter method cannot have its value changed. Look what happens when we try to change the getter here (it doesn't change):
const obj = {
methodNormal() {
return 5;
},
get methodGetter() {
return 5;
}
};
obj.methodNormal = "red";
obj.methodGetter = "blue";
console.log(obj);
2 . Secondly, with a normal property you have the luxury of either returning the function i.e. obj.methodNormal or returning the function executing i.e. obj.methodNormal(). With getter functions you do not have the luxury of returning the function. You can only do obj.methodGetter which executes that function. The code snippet below demonstrates that.
const obj = {
methodNormal() {
return 5;
},
get methodGetter() {
return 5;
}
};
let captureNormalMethod = obj.methodNormal;
let captureGetterMethod = obj.methodGetter;
console.log(captureNormalMethod);
console.log(captureGetterMethod);
Both these qualities - being unchangeable & unable to be captured in a new variable or property - contribute to getter functions having a sense of 'hiddenness'. Now you can understand what people mean when they say getters are 'read-only' properties!
Further reading:
What I've been referring to as 'normal properties' are called data properties (good article).
Getter methods are an example of what are called accessor properties (good article).

A method can only return data a property can also have a setter

This is useful if you want to expose read-only properties. It isn't just a function to the caller.

In your example, both do the same thing, but the importance is not how they are alike but how they are not. You can, for example, pass the method version as an argument somewhere else to do some lazy execution, but the getter won't work like that.
const obj = {
lazyProp(parameter) {
return parameter + 2;
}
}
function evaluateLazy(fn) {
return fn()
}
evaluateLazy(obj.lazyProp)

One useful scenario is where you want to use regular property access on all your properties, but need to invoke a method to calculate a given value. For example take the following example which doesn't use a getter. It ends up printing the function, not the return value of the function. Without a getter, we would need to handle a specific case where the value is a function, and then invoke it.
const obj = {
x: 10,
y: 20,
total() {
return this.x + this.y;
}
}
const prettyPrintObj = obj => {
for(let key in obj) {
console.log(`${key.toUpperCase()} is ${obj[key]}`);
}
}
prettyPrintObj(obj);
However, with a getter, we can use the same function, and don't need to handle specific cases for where obj[key] is a function, as simply doing obj[key] on a getter will invoke the function for you:
const obj = {
x: 10,
y: 20,
get total() {
return this.x + this.y;
}
}
const prettyPrintObj = obj => {
for(let key in obj) {
console.log(`${key.toUpperCase()} is ${obj[key]}`);
}
}
prettyPrintObj(obj);

Related

Re-use arrow function to write values

If I have an arrow function like:
let fn = x => x.contact.name;
Then, I can re-use that fn to retrieve the value as:
function read(obj) {
return fn(obj)
}
But how can I set values of an object using the property expression in fn?
function write(obj, newVal) {
// intending to set obj.contact.name = newVal using fn
}
Edit:
(To give a little bit of background)
I am writing a JS library where consumers would provide a lambda expression like above (fn), and I provide a read/write functionality somewhere based on the expression. Right now I am using fn.toString() and string manipulation as a temporary solution.
Normally, lenses need two callbacks, get and set, because (in javascript at least) functions are not permitted to return references. For simple cases, like getting/setting a property, you can provide a shortcut function that would return a getter/setter pair. For example:
function prop(path) {
return {
get(obj) {
return path.split('.').reduce((o, p) => o[p], obj)
},
set(val) {
return function (obj) {
let p = path.split('.')
let k = p.pop()
let r = p.reduce((o, p) => o[p], obj)
r[k] = val
}
}
}
}
//
obj1 = { contact: { name: 'one' }}
obj2 = { contact: { name: 'two' }}
let lens = prop('contact.name')
console.log([obj1, obj2].map(lens.get));
[obj1, obj2].forEach(lens.set('hello'));
console.log([obj1, obj2].map(lens.get));
See also: https://randycoulman.com/blog/2016/07/12/thinking-in-ramda-lenses/
The below two functions are almost entirely the same:
let fn = x => x.contact.name;
function fn(x) {
return x.contact.name;
}
As such, you can't use a function that returns an object property to set the object property.
Instead consider the following two functions:
let fn = (x, newVal) => {
if (newVal) x.contact.name = newVal;
return x.contact.name;
};
/* - This function included just for reference -
function fn(x, newVal) {
if (newVal) x.contact.name = newVal;
return x.contact.name;
}
*/
let myObj = { contact: { name: "Jess" } };
console.log(fn(myObj)); // name currently
fn(myObj, "John"); // set new name
console.log(myObj); // show new name on object
Maybe the following will do what you want?
I had to change the "lambda" expression a little bit, as it would not make much sense to give the return value of the original function to a potential "write" function, as it was a value and not a reference. My "new" function definition of fn works differently: it returns an array with two elements:
the "parent" object containing a particular attribute
the name of the attribute of the object.
The functions read() and write() can then pick up the return values of fn(o) and perform their particular actions accordingly.
let fn = x => [x.contact,"name"];
const read=o=>fn(o)[0][fn(o)[1]] // get the attribute as defined in fn
,write=(o,v)=>fn(o)[0][fn(o)[1]]=v; // set the attribute as defined in fn
const o={contact:{name:"Harry",country:"England"}};
console.log(read(o));
write(o,"Hermiony");
console.log(read(o));
// change the lambda function:
fn = x => [x.contact,"country"];
write(o,"Germany");
console.log(read(o));
console.log(o);

Javascript Know when object value is modified

For any variable or property thereof, is there a way to know when its value is set?
For example, say I have:
let x = { 'a': 1, 'b': 2 };
// x.a's set operation is linked to a method
x.a = 3; // the method is automatically called
Is there a way I can call a function when a's value is changed? Lots of code would be changing this value; I don't want to add the method call all over the place.
I'm aware of Proxies, but to use them seems to require a separate variable. Meaning, x can't be a proxy of itself.
Preferably this technique would work with primitive and non-primitives.
x can't be a proxy of itself
Sure it can. You can change the variable to point to a Proxy by simply doing
x = new Proxy(x, handler)
Primitive example:
const handler = {
set: function(obj, prop, value) {
console.log('setting prop: ', prop, ' to ', value)
obj[prop] = value;
return true;
}
};
let x = { 'a': 1, 'b': 2 };
x = new Proxy(x, handler);
x.a = 3; // the method is automatically called
To be honest, use Proxy if you can
If you really can't use Proxy, you could achieve this using using setters and getters
Though it does mean re-declaring your original x object, I assume it's declared inline like the Minimal, Complete and Verifiable Example in your question
let x = {
_a: 1,
_b: 2,
get a() {
return this._a;
},
get b() {
return this._b;
},
set a(value) {
console.log(`changing a from ${this._a} to ${value}`);
this._a = value;
},
set b(value) {
console.log(`changing b from ${this._b} to ${value}`);
this._b = value;
}
};
x.a = 3;

Is there a less cumbersome method of retrieving getter and setter methods that are somewhere in the prototype chain?

I have a reference to an object, which has a getter and setter for a certain property somewhere in its prototype chain. I want to get a reference to the getter and setter methods, and the object they're on. I know I can do it by manually iterating over every prototype object and checking hasOwnProperty, as in the following snippet:
const obj2 = (() => {
const baseProto = {
get prop() {
return 'propval';
},
set prop(newVal) {
console.log('setting...');
}
};
const obj1 = Object.create(baseProto);
return Object.create(obj1);
})();
// From having a reference to obj2, want to get the getter or setter methods,
// and want to get the object they're on, without invoking them:
let currentObj = obj2;
const propToFind = 'prop';
let get, set, foundProto;
while (currentObj) {
if (currentObj.hasOwnProperty(propToFind)) {
foundProto = currentObj;
({ get, set } = Object.getOwnPropertyDescriptor(currentObj, propToFind));
break;
}
currentObj = Object.getPrototypeOf(currentObj);
}
if (foundProto) {
console.log('Found:', get, set, foundProto);
}
This seems rather cumbersome, and while loops are ugly. Of course, the getter and setter can be invoked, with the calling context of the current object, with very simple code like
obj2.prop = 'newVal'; // invoke setter
const val = obj2.prop; // invoke getter
But that invokes the functions without being able to interact with them (or the prototype object they're on).
Is there any clearer, shorter way of achieving what I'm doing in the snippet above?
This seems rather cumbersome, and while loops are ugly
I don't think this is cumbersome, it's just what you have to do when trying to find a property anywhere on the prototype chain.
You don't have to write a while loop of course, the iteration can be easily expressed as a for loop:
let get, set, foundProto;
for (let currentObj = obj2; currentObj; currentObj = Object.getPrototypeOf(currentObj)) {
if (currentObj.hasOwnProperty('prop')) {
foundProto = currentObj;
({ get, set } = Object.getOwnPropertyDescriptor(currentObj, 'prop'));
break;
}
}
if (foundProto) {
console.log('Found:', get, set, foundProto);
}
You can of course also write a helper function to do this, like
function getInheritedPropertyDescriptor(obj, prop) {
for (; obj != null; obj = Object.getPrototypeOf(obj)) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
return { foundProto: obj, ...Object.getOwnPropertyDescriptor(obj, prop) };
}
}
return null;
}
var result = getInheritedPropertyDescriptor(obj2, 'prop');
if (result) {
console.log('Found:', result.get, result.set, result.foundProto);
}

Should one use getters and setters for private variables?

I'm using JavaScript objects like this:
var obj = new Foob;
should I pretend like there is a private way and do:
obj.get('foo');
or should I just try to access directly as :
obj.foo
You can actually have variables which can only be accessed through setters and getters in Javascript:
function Foob(){
var foo = 5;
this.getFoo = function(){
return foo;
}
this.setFoo = function(newFoo){
foo = newFoo;
return this;
}
}
var obj = new Foob;
obj.getFoo(); // 5
obj.foo; // undefined
Or if you want a generic getter/setter:
function Foob(){
// You can set default values in the data object
var data = {};
this.get = function(key){
return data[key];
}
this.set = function(key, value){
data[key] = value;
return this;
}
}
One least known feature of Javascript is that it supports getters and setters natively.
When defining a property, you can either define it simply by :
someObj.prop = 12;
OR you can define getters and setters , using Object.defineProperty and the reserved words get and set :
Object.defineProperty ( someObj, "prop" ,
{ get : function () { return ??; } ,
set : function (val) { /* store val */ } } ;
A way of using such getters/setters inside a 'class' to get a private variable would be :
var MyClass = function() {
var _private = 5;
Object.defineProperty(this, "public", {
get : function() { return _private; },
set : function(val) { _private=val; }
});
return this;
};
var anInstance = new MyClass();
anInstance.public = 12 ;
console.log(anInstance.public) ; // writes 12
console.log(anInstance._private) ; // writes undefined.
so you have a truly private variable, armed with a getter and a setter.
Obviously, there is not much interest to use getters/setters code , unless you want to make bound-checking/type-checking or have a another specific reason.
I used to like the idea of getters and setters when I started using object-oriented JavaScript heavily, but that is because I was coming from a Java background.
ES5 supports getters and setters through a special syntax
See John Resig's explanation:
http://ejohn.org/blog/javascript-getters-and-setters/
My take is to think about why getters/setters are useful. It's so one has a way to encapsulate a variable's access so that it can be intercepted / controlled. If calling code directly mutates another object's instances variables, then you can't change it transparently. Needing to catch all of these mutations requires changing variable scope, adding a getter and setter and altering all calling code.
However, this syntax is transparent to calling code. Therefore, you can simply allow a property to be directly controlled and then if you need to intercept it, to say add a console.log for debugging, you can ADD a getter and setter and it will just work.
function Foob() {
}
Foob.prototype = {
get foo() {
return this._foo;
},
set foo(foo) {
this._foo = foo;
}
};
var o = new Foob();
console.log(o.foo);
The downside to the getter/setter syntax is that it doesn't actually make your variable private, it only "hides" it so that you can use some secret internal name, unless you define it within the constructor as Vincent indicated.
To get truly private:
function Foob() {
var foo;
this.__defineGetter__('foo', function () {
return foo;
});
this.__defineSetter__('foo', function (_foo) {
foo = _foo;
});
}
var o = new Foob();
console.log(o.foo);

Is it possible to create some property in JavaScript that contain some methods?

Please look at my required JavaScript.
var someVariable = new SomeDataType();
// I can directly access value of its property.
someVariable.someProperty = "test";
alert(someVariable.someProperty); // <- this command must should "test"
// However, I have some methods in above property
// I want to validate all rule in this property.
someVariable.someProperty.isValid(); // <- this method will return true/false
Is it possible for doing this in current version of JavaScript?
UPDATE
Please look as my answer!
Yes, you can assign Javascript functions as properties like this:
someVariable.someProperty = function (arg1, arg2) {
// function code goes here
};
This is the method using function literals.
Another method is to use function instances like this:
someVariable.someProperty = new Function (arg1, arg2, code);
Note that in the second method, the code goes in as the last parameter and the Function keyword has a capitalized 'F' as against method 1 where the 'f' is small.
Further, creating a function instance inside a loop etc. will create an entire new instance to assign which is inefficient in memory. This problem does not arise while using the function literal method.
You can't (and probably shouldn't) treat objects like that in JavaScript. As someone else mentioned, you can override the toString() method to get half of the functionality (the read portion), but you cannot use the assignment operator on an object like that without overwriting the object.
You should choose a different approach, like using nested objects (as CMS suggested).
Its possible, but with the below change in your code
function SomeDataType(){
var localProperty="";
this.someProperty = function(txt){
if (arguments.length==0)
return localProperty;
else
localProperty=txt;
}
this.someProperty.isValid = function(){
return (localProperty!="") ? true : false;
};
}
instead of defining someProperty as a property, define this as function which sets value to the local property if any value is passed or it ll return that property value if no argument is given.
var someVariable = new SomeDataType();
someVariable.someProperty("test");
alert(someVariable.someProperty());
var isValid = someVariable.someProperty.isValid();
this is how you need to access the SomeDataType object.
someVariable.someProperty = [ test, anotherFunc, yetAnotherFunc];
someVariable.somePropertyAllValid= function() {
for(var prop in someVariable.someProperty) {
if(!prop()) return false;
}
return true;
};
someVariable.somePropertyAllValid();
I just found the answer. It's very simple & clean.
function createProperty(value, defaultValue, ruleCollection)
{
this.value = value;
this.defaultValue = defaultValue;
this.ruleCollection = ruleCollection;
}
createProperty.prototype.toString = function()
{
return this.value;
};
var someVariable =
{
someProperty: new createProperty
(
'currentValue',
'defaultValue',
null
)
};
For testing, you can use something like my following code.
var test = ">>" + someVariable.someProperty + "<<";
// this alert must shows ">> currentValue <<"
alert(test);
someVariable =
{
someProperty: new createProperty
(
7,
5,
null
)
};
test = someVariable.someProperty + 3;
// This alert must shows "10"
alert(test);
I just test it on FF 3.5 & IE 8. It works fine!
Update
Oops! I forget it. Because this technique returns object reference for this property. So, it's impossible to directly set property data. It isn't my final answer.
Perhaps this would be of help:
var SomeVar = {
someProperty : {
value : 7,
add : function (val) {
this.value += parseInt(val, 10);
return this;
},
toString : function () {
return this.value;
}
}
}
alert(SomeVar.someProperty.add(3));

Categories