The getter method returns the value of the attribute. The setter method takes a parameter and assigns it to the attribute. Getters and setters allow control over the values. You may validate the given value in the setter before actually setting the value.
It is not necessary to write getter or setter for all private variables. It is just a good practice. But without any public function you can not access the private data(variable) of the class.
Getter and setter methods (also known as accessors) are dangerous for the same reason that public fields are dangerous: They provide external access to implementation details. What if you need to change the accessed field's type? You also have to change the accessor's return type.
getters and setter can have validation in them, fields can't. using getter you can get subclass of wanted class. getters and setters are polymorphic, fields aren't. debugging can be much simpler, because breakpoint can be placed inside one method not near many references of that given field.
This is currently possible in environments with Proxies. That would be node > 0.6 run as node --harmony_proxies
or >0.7 with node --harmony
. Chromium Canary (not sure if it's out of that yet) in about:flags at the bottom, experimental javascript. Firefox has had it for a while with no flags.
So this probably won't work when ES6 becomes more official, but it works to an extent now.
var target = (function(){
var handler = Proxy.create(Proxy.create({
get: function(r, trap){
return function(name,val,c,d){
if (trap === 'get' || trap === 'set') {
name = val;
val = c;
}
console.log('"'+trap + '" invoked on property "'+name+'" ' + (val?' with value "'+val+'"':''));
switch (trap) {
case 'get': return target[name];
case 'set': return target[name] = val;
case 'has': return name in target;
case 'delete': return delete target;
case 'keys': return Object.keys(target);
case 'hasOwn': return Object.hasOwnProperty.call(target, name);
case 'getPropertyDescriptor':
case 'getOwnPropertyDescriptor': return Object.getOwnPropertyDescriptor(target, name);
case 'getPropertyNames':
case 'getOwnPropertyNames': return Object.getOwnPropertyNames(target);
case 'defineProperty': return Object.defineProperty(target, name, val);
}
}
}
}))
var target = {
x: 'stuff',
f: { works: 'sure did' },
z: ['overwritten?']
};
with (handler){
var z = 'yes/no';
if (x) {
//x
} else {
x = true;
}
console.log(f.works);
if (f.works) {
f.works = true;
delete f;
}
}
return target
})()
// "getPropertyDescriptor" invoked on property "z"
// "getPropertyDescriptor" invoked on property "z"
// "getPropertyDescriptor" invoked on property "x"
// "get" invoked on property "x"
// "getPropertyDescriptor" invoked on property "console"
// "getPropertyDescriptor" invoked on property "f"
// "get" invoked on property "f"
// sure did
// "getPropertyDescriptor" invoked on property "f"
// "get" invoked on property "f"
// "getPropertyDescriptor" invoked on property "f"
// "get" invoked on property "f"
// "getPropertyDescriptor" invoked on property "f"
target: { x: 'Stuff', f: { works: true }, z: ['overwritten?'] }
Hit or miss and you need to take care not to blow up your browser by simply looking at a Proxy in the debugger. I had to wrap that thing in a closure to keep the proxy from ending up in the global scope or it crashed the frame every single time. Point is that it works to some extent, where nothing else does.
It looks like the answer is No. I have been searching for behavior like this for quite a while. I have not been able to come up with any passable solution. This SO question seems similar. Python has the nice locals
keyword.
Since you state you want similar behavior to window/global
, I assumed you want this within a given context other that window/global
. An easy way to do this is by using the with
statement in combination with a local
object and a define
function which implement Object.defineProperty
with local
as target. You than simply place your own code within the with
block.
IMPORTANT: with
overloads the native local variables (var, let, const
). Because of this it's very important to keep clear code, and to prevent duplicate names within the scope and parent/child contexts.
Lets start of with the context, in this case I use a closure, but this could also be a function, constructor or any other context.
// This closure represents any function, class or other scoped block.
(function (){
}());
Next we add the storage container and the define
function. This is basically what you should always start with if you want access the local properties from anywhere in your code (within this scope).
// This is where we store the local property. (except: var, let, const)
const local = {};
// The define function is used to declare and define the local properties.
function define(name, descriptor){ Object.defineProperty(local, name, descriptor); }
Now you can place any code before the with
statement but for this example we'll only add code that requires local
in some way so the next step is creating the with
statement.
// This with statement extends the current scope with local.
with(local){
// This is where your code goes.
}
Now the outer structure of the with
statement is ready, and we can start adding code inside the with statement.
All code placed within the with
statement's block has access to the properties of local
as if they where defined with for instance var
, including properties defined within the with
statement.
There are several ways to work with the properties of local
. The easiest way to define a property is by setting it within 'local' directly. This only needs to be done once, after that the property is accessable by just it's name.
local.setDirectly = "directly set value";
console.log(setDirectly); // logs "directly set value"
An other way to define a property, but than with support for get/setters
as well as options on enumerabiliy and write access, is to use the define
function. Expect the same behavior as from Object.defineProperty
.
You could for instance add a time
property that returns the current time.
define("time", {
get: function(){
var date = new Date();
return date.getHours() + ":" + ("0" + date.getMinutes()).substr(-2);
}
})
console.log(time);
Or you could create a counter property that increments each time it's accessed, placed within a nested closure to protect the counters own variable from unwanted changes.
(function (){
var counterValue = 0;
define("count", {get: function(){ return counterValue++ }});
}());
console.log(count); // logs 0
console.log(count); // logs 1
When you combine all this you will get something similar to the following code
// This closure represeents any function, class or other scoped block.
(function(){
// This is where we store the local property. (except: var, let, const)
const local = {};
// The define function is used to declare and define the local properties.
function define(name, descriptor){ Object.defineProperty(local, name, descriptor); }
// This with statement extends the current scope with local.
with(local){
// This is where your code goes.
// Defining a variable directly into local.
local.setDirectly = "directly set value";
console.log(setDirectly); // logs "directly set value"
// Defining local properties with the define function
// For instance a time variable that return the current time (Hours:Minutes)
define("time", {
get: function(){
var date = new Date();
return date.getHours() + ":" + ("0" + date.getMinutes()).substr(-2);
}
})
console.log(time); // logs HH:MM
// Or a counter property that increments each time it's been accessed.
(function (){
var counterValue = 0;
define("count", {get: function(){ return counterValue++ }});
}());
console.log(count); // logs 0
console.log(count); // logs 1
console.log(count); // logs 2
console.log(count); // logs 3
}
}());
Like I mentioned before, it is important to understand the implications of using the with
statement. More information on with
can be found at MDN - with. As the question states, it's a search to how you could, not how you should. Use the information on MDN to see if it fits your situation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With