why to use accessor ( getters and setters)? - javascript

var obj = {
get foo(){//Why can't I use argument ^!^
return 'getter';
},
set foo(value){
console.log('setter: '+value);
}
}
> obj.foo = 'bla'
setter: bla
>obj.foo
'getter'
So, I wanted to get the value of foo when it is set:
>obj.foo = 'bla'
setter: bla
>obj.foo
getter: bla
Using like this:
get foo(value){//but alas, I can't use any argument in getter
return 'getter: '+value;
}
Can we not get the value what we set after? I think I'm not understanding the usage of accessor, why do we use it specially for?

may be this would explain it more to you
var obj = {
get foo(){
console.log('getter');
return this._foo;},
set foo(v){
console.log('setter');
this._foo = v;
}
}
while getting a value from the object passing a parameter wont mean anything.
while setting a value passing a parameter would mean and represent the value that needs to be set.
Chrome console. AFter initializing the object
obj.foo = "UI"
setter
"UI"
obj.foo
getter
"UI"

The getter is there simply to apply additional logic during all assignments to that property. As a getter, it doesn't make sense for it to receive a parameter, since there's no opportunity to pass one during the get operation.
If you want the getter and setter to use the current value of the property, what you need to do is store it somewhere that it is accessible to both getter and setter.
This could be on the same object, on a different object, or via a closure referenced variable.
var obj = {
get foo(){
return this.___foo___
},
set foo(value){
this.___foo___ = value;
}
}
So now the get and set are just a controlled access to the ___foo___ property.

You need setters/getters, when you need implement some non-standart logic for property.
For example, next object accumulate all assignments:
var obj = {
_a:0,
get a(){ return this._a;},
set a(value){this._a+=value;}
}
obj.a = 5;
obj.a = 7;
obj.a
12
Also this useful, when you want to write some proxy object,
for example getter resolves data from remote server, and setter post your data to it.
If you need simple property, just use
{a:0}
instead of
{_a:0,get a(){return this._a;},set a(v){this._a=v;}}
Other snippet, with js 'privates':
function Obj(key) {
var private = 0;
var authorized = false;
return {
set token(v) {authorized = (v===key);},
set data(v) {if(authorized) private = v;}
get data() {return authorized?private:undefined;}
}
}
obj = new Obj('pass');
obj.data = 5; // no effect
obj.token = 'pass';
obj.data = 'Data'; //Now works!
///...

Related

Javascript Set:Get Pointless?

I have been experimenting with getters and setters with the following pattern:
var mytab = {
_tab: undefined,
get: function () {
return this._tab;
},
set: function (tab) {
this._tab = tab;
return tab;
}
}
My question is, given you have to access those methods explicitly, ie:
mytab.get();
mytab.set('thistab');
Why bother having get or set at all? Why not call them whatever you like? ie:
var mytab = {
_tab: undefined,
getthetab: function () {
return this._tab;
},
setthetab: function (tab) {
this._tab = tab;
return tab;
}
}
I may have missed some fundamental principle here, but both these objects behave exactly the same.
I assumed having special 'setters' and 'getters' would allow the object to be modified using it's object name, ie:
var a = mytab;
mytab = 'thistab';
Or even:
var a = mytab();
mytab() = 'thistab';
This is what I expected, and what I wanted, however those instructions give errors, namely that mytab() is not a function.
I would appreciate some clarity on what special significance the set and get object methods actually have.
In your first example, you haven't declared getters/setters. You've created an object with two methods called get and set.
To declare getters/setters, you'll have to choose an arbitrary name, and prefix it with get or set, like:
var mytab = {
_tab: undefined,
get tab() {
return this._tab;
},
set tab(tab) {
this._tab = tab;
return tab;
}
}
In this case, they form a so-called accessor property, that has the chosen name:
console.log(mytab.get) //undefined
console.log(mytab.set) //undefined
mytab.tab = 'foo'
console.log(mytab._tab) //foo
mytab._tab = 'bar'
console.log(mytab.tab) //bar
console.log(Object.getOwnPropertyDescriptor(mytab, 'tab')) /*
{
get: function(){...},
set: function(tab){...},
...
}
*/
However, you cannot overload operators or otherwise define a single getter/setter pair for your objects, that would allow you to assign a value or read a value from the object itself.
You can only define getters/setters for the properties on the object.
So,
var a = mytab
or
mytab = a
cannot be intercepted, and doesn't do what you expect (the first assigns the object itself to another variable (a), while the second reassigns the variable mytab with the value of a without even affecting / interacting with the object).
The following use case can illustrate advantage of using getters and setters
var person = {
firstName: "John",
lastName: "Doe",
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
};
console.log(person.fullName);
Using getter we could use get fullName as if it was a property of person without the need of maintaining separate field.

Calculate property at call time without using function invocation syntax

is there any pattern I can use to compute a variable at call time (like a property in C#)
var A = (function() {
var self = {};
var a = 0;
self.x = function (){
a = a+1;
return a;
};
return self;
});
Normally, the call would be:
var bla = A.x();
but I would like to evaluate it and get its value as a property:
var bla = A.x;
console.log(bla); // prints "1"
I don't want the () operator, but I still want to compute/calculate the property value when I access A.x
You must be talking about getters and setters.
Those are function that gets called 'behind the scene' when you read (get) or assign (set) an object property.
They are quite standard (IE>=9), but have a somehow complicated syntax.
Look mdn :
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/defineProperty
A (verbose) example, implementing a property backed by a variable :
var A = (function() {
var self = {};
var x = 0;
var xPropertyDescriptor = { enumerable : true ,
get : function() { return x },
set : function(val) { x=val } };
Object.defineProperty(self, 'x', xPropertyDescriptor);
return self;
} () );
After the call to defineProperty, you can access 'x' just like a regular property, and either the get or set function will get called.
So basically, writing :
A.x = 12 // will call the getter : function(12) { x=12 }, and set the variable.
Notice that you can define a getter/setter on the prototype for later having more efficient object creation (otherwise you have to define them in the constructor for each instance.).
Yet defining the properties in the constructor is the only way to use a closure (like in the example above) to have truly private members.
In use :
var res = A.x ; // res == 0
A.x = 12 ;
var res2 = A.x // res2==12
Yes, you can use
var bla = A.x;
console.log(bla());
//or
console.log(bla.call());
There are a couple more options, but these are the most straightforward ones.
As of Ecmascript5, there's a defineProperty method you can use to define getters and setters for properties.
Take a look on this: http://robertnyman.com/javascript/javascript-getters-setters.html
So, as detailed in the example linked, you can define a getter/setter like this:
var myTest = {
val : "Initial Value",
get value () {
return this.val + " Modified";
},
set value(newVal) {
this.val = newVal;
}
};
myTest.value // "Initial Value Modified"
myTest.value = "foo"
myTest.value // "foo Modified"
Good luck.
PS: You should keep in mind the compatibility issues this may bring. Please read the link, you'll find detailed info there.

Overriding assignment operator in JS

var myObject = {"myKey" : "myValue"}
typeof(myObject.myKey) returns `string`
myObject.myKey = "newValue"
console.log(myObject.myKey) prints newValue
This is the expected behavior. But, similar value writes do not work for document.cookie
typeof(document.cookie) returns `string`
But performing document.cookie = "value=123", appends to document.cookie string rather than set its value to value=123
So, how is assignment to document.cookie overridden?
document.cookie is a little magical, but depending on your browser constraints, you an use Object.defineProperty to define properties that have different get and set behavior.
For example:
var obj = {};
Object.defineProperty(obj, "data", {
get: function() {return this.val; },
set: function(val) { this.val = JSON.stringify(val); }
});
obj.data = {a:1}; // Set as an object...
console.log(obj.data) // but retrieve as string '{"a":1}'
For example, to do something similar to the cookie example, you could make a function like:
var mixinExtender = (function mixinExtender(target) {
var rawValue = {};
Object.defineProperty(target, "data", {
get: function() { return JSON.stringify(rawValue); },
set: function(val) {
for(var key in val) {
rawValue[key] = val[key];
}
}
});
})
This will mixin in a data property that will extend the setter value into a private object. The getter will return a serialized version of it. Then you could use it with:
var obj = {};
mixinExtender(obj);
obj.data = {a:1}; // Add "a" key
obj.data = {b:2}; // Add "b" key
console.log(obj.data) // > {"a":1,"b":2}
Browser-supplied host objects behave in ways that are not constrained by the semantics of the language. That is, document looks like a JavaScript object, but it's not. It's part of the runtime environment.
The JavaScript spec is written in terms of various internal "method" descriptions. Host objects like window and document have special versions of those internal methods. Thus, the runtime follows the spec as to how the = assignment process works, but the internal method [[Put]] is simply special.

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