OOP Javascript - Getter & Setters - javascript

I have been studying some oop principles in javascript but I have some problems related to restriction of my code. In C# and Java we can easily do this kind of restrictions and see the results but in javascript I dont understand completely what is going on under the hood.
Anyway, I am trying to create a code that people can not change it outside basically. This is why I learnt that we should use local variables instead of creating object properties and by using getFunction as a method we can just read from outside.
Another approach is using "Object.defineProperty" (Getters & Setters)
All these work perfectly for primitive types but I guess we have some problem. I can not restrict my code read only. Without writing any setter methods I can change values because of the reference type feature. So how can I approach this problem ?
// Abstraction
function Circle(radius) {
this.radius = radius;
let defaultLocation = { x: 0, y: 0 };
let color = 'red';
this.getDefaultLocation = function() {
return defaultLocation;
}
this.draw = function(){
for (let key in defaultLocation) {
console.log(key, defaultLocation[key]);
}
console.log('drawn');
};
// This can be changed from outside without set(){}
Object.defineProperty(this, 'defaultLocation', {
get(){
console.log('get function');
return defaultLocation;
}
});
// This needs set(){} to be changed
Object.defineProperty(this, 'color', {
get(){
console.log('get function');
return color;
},
set(value){
color = value;
}
});
}
const circle = new Circle(10);

You said
I am trying to create a code that people can not change it outside basically.
Refer to another stack overflow question. You can use the ES6 classes syntax, like so
class Circle {
constructor(radius) {
this.radius = radius;
}
get defaultLocation() {
return { x: 0, y: 0 }; // Read-only
}
get color() {
return 'red'; // Read-only
}
}
let test = new Circle(2);
console.log(test.defaultLocation); // {x: 0, y: 0}
test.defaultLocation = 10; // No error but won't do anything
console.log(test.defaultLocation); // {x: 0, y: 0}

If you only want a only a get() function, you can use a constructor function (not the latest ECMAScript syntax) with block-scope (let) variables being private instance variables. The key is NOT to use the this keyword.
function Circle(r = 0) {
// Private
let raduis = r
// Public
this.getRaduis = function() {
return raduis
}
}
let circle = new Circle(1)
console.log(circle.getRaduis())
console.log(circle.raduis)
Output:
1
undefined
You can add another option to defineProperty. This effectively creates a final static constant. Just set writeable = false.
Object.defineProperty(Circle, 'constants', {
value : {
constant : 0,
operation : () => {
console.log('Some non-primitive')
}
},
writable : false,
enumerable : false,
configurable : false
});
Circle.constants.operation()
Output:
Some non-primitive
See the "Writable attribute" section of the documentation.

Related

Unable to have get and set method in Object Constructor (not in class) in JavaScript? [duplicate]

I recently read about the fact that there is a possibility of defining getters/setters in JavaScript. It seems extremely helpful - the setter is a kind of 'helper' which can parse the value to be set first, before actually setting it.
For example, I currently have this code:
var obj = function(value) {
var test = !!value; // 'test' has to be a boolean
return {
get test() { return test },
set test(value) { test = !!value }
};
};
var instance = new obj(true);
This code always converts value to a boolean. So if you code instance.test = 0, then instance.test === false.
However, for this to work you have to actually return an object, which means that the new instance is not of type obj but just is a plain object. This means that changing the prototype of obj has no effect on instances. For example, this does not work - instance.func is undefined:
obj.prototype.func = function() { console.log(this.value); };
because instance is not of type obj. To get the prototype functions work, I guess I should not return a plain object, but rather not return anything so that instance would just be of type obj, like a regular constructor works.
The problem then is how to implement getters/setters? I can only find articles describing how to add these to an object, not as being part of the constructor of a custom type.
So how do I implement getters/setters in the constructor so as to be able to both use getters/setters and extending the prototype?
You can't do that.
You can set setter/getters for properties of objects though. I advice you use ES5 Object.defineProperties though. of course this only works in modern browsers.
var obj = function() {
...
Object.defineProperties(this, {
"test": {
"get": function() { ... },
"set": function() { ... }
}
});
}
obj.prototype.func = function() { ... }
var o = new obj;
o.test;
o.func();
Usually you want class methods. The answer by #Raynos on May 7, 2011 gets the job done, but it defines an instance method, not a class method.
The following illustrates a class definition with a the getter and setter being part of the class. This definition is a lot like the answer by #Raynos, but with two differences in the code: (1) The "defineProperties()" action has been moved out of the constructor. (2) The argument to "defineProperties()"as been changed from the instance object "this", to the constructor's prototype object.
function TheConstructor(side) {
this.side = side;
}
Object.defineProperties(TheConstructor.prototype, {
area: {
get: function() { return this.side * this.side; }
,set: function(val) { this.side = Math.sqrt(val); }
}
});
// Test code:
var anInstance = new TheConstructor(2);
console.log("initial Area:"+anInstance.area);
anInstance.area = 9;
console.log("modified Area:"+anInstance.area);
Which produces these results:
initial Area:4
modified Area:9
Although usually the distinction between class versus instance
definition is just a matter of style, there is a purpose to
good style, and there is a case where the distinction matters:
the memoized getter. The purpose for a memoized getter is
described here: Smart/self-overwriting/lazy getters
Define the getter at the class level when the memoized value is to
pertain to the entire class. For example, a configuration file
should be read only once; the resulting values should then apply
for the duration of the program. The following sample code
defines a memoized getter at the class level.
function configureMe() {
return 42;
}
Object.defineProperties(TheConstructor.prototype, {
memoizedConfigParam: {
get: function() {
delete TheConstructor.prototype.memoizedConfigParam;
return TheConstructor.prototype.memoizedConfigParam = configureMe();
}
,configurable: true
}
});
// Test code:
console.log("memoizedConfigParam:"+anInstance.memoizedConfigParam);
Produces:
memoizedConfigParam:42
As can be seen in the example, memoized getters have the
characteristic that the getter function deletes itself,
then replaces itself with a simple value that
(presumably) will never change.
Note that 'configurable' must be set to 'true'.
Define the getter at the instance level when the memoized value
depends upon the contents of instance. The definition moves
inside the constructor, and the object of attention is 'this'.
function TheConstructorI(side) {
this.side = side;
Object.defineProperties(this, {
memoizedCalculation: {
get: function() {
delete this.memoizedCalculation;
return this.memoizedCalculation = this.expensiveOperation();
}
,configurable: true
}
});
}
TheConstructorI.prototype.expensiveOperation = function() {
return this.side * this.side * this.side;
}
//Test code:
var instance2 = new TheConstructorI(2);
var instance3 = new TheConstructorI(3);
console.log("memoizedCalculation 2:"+instance2.memoizedCalculation);
console.log("memoizedCalculation 3:"+instance3.memoizedCalculation);
Produces:
memoizedCalculation 2:8
memoizedCalculation 3:27
If you want to guarantee (rather than presume) that the memoized
value will never be changed, the 'writable' attribute needs to
be changed. That makes the code a bit more complicated.
function TheConstructorJ(side) {
this.side = side;
Object.defineProperties(this, {
memoizedCalculation: {
get: function() {
delete this.memoizedCalculation;
Object.defineProperty( this, 'memoizedCalculation'
,{ value : this.expensiveOperation()
,writable : false
});
return this.memoizedCalculation;
}
,configurable: true
}
});
}
TheConstructorJ.prototype.expensiveOperation = function() {
return this.side * this.side * this.side;
}
//Test code:
var instanceJ = new TheConstructorJ(2);
console.log("memoizedCalculation:"+instanceJ.memoizedCalculation);
instanceJ.memoizedCalculation = 42; // results in error
Produces:
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
The OP's original question, from March 7, 2011, presented basic
getter and setter syntax, noted that it worked on an object but
not on 'this', and asked how to define getters and setters within
a constructor. In addition to all the examples above, there is
also a "cheap-shot" way of doing it: create a new object within
the constructor, like the OP did, but then assign the object to
be a member within 'this'. So, the original code would look like
this:
var MyClass = function(value) {
var test = !!value; // 'test' has to be a boolean
this.data = {
get test() { return test },
set test(value) { test = !!value }
};
};
var instance = new MyClass(true);
// But now 'data' is part of the access path
instance.data.test = 0;
console.log(instance.data.test);
Produces:
false
Believe it or not, I have actually run into situations where
this "cheap-shot" is the best solution. Specifically, I used this
technique when I had records from several tables encapsulated within
a single class, and wanted to present a unified view as though
they were a single record called 'data'.
Have fun.
IAM_AL_X
Update for ES6 -- have a look at section 19.3.1 of Alex Rauschmayer's book Exploring ES6 http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-data which demonstrates how to use WeakMaps with getters and setters to hold private data. Combining with section 16.2.2.3 http://exploringjs.com/es6/ch_classes.html#leanpub-auto-getters-and-setters would result in something like
# module test_WeakMap_getter.js
var _MyClassProp = new WeakMap();
class MyClass {
get prop() {
return _MyClassProp.get( this );
}
set prop(value) {
_MyClassProp.set( this, value );
}
}
var mc = new MyClass();
mc.prop = 5 ;
console.log( 'My value is', mc.prop );
$ node --use_strict test_WeakMap_getter.js
My value is 5
function Obj(value){
this.value = !!value;
}
Obj.prototype = {
get test () {
return this.value;``
},
set test (value) {
this.value = !!this.value;
}
};
var obj = new Obj(true);
I know this might be extremely late but I figured out a different way to accomplish what you want and for the sake of people, like myself, googling for an answer to this here it is.
function Constructor(input){
this.input = input;
}
Object.__defineGetter__.call(Constructor.prototype, "value", function(){
return this.input * 2;
});
var test = new Constructor(5);
alert(test.value) // 10
I've tested this in chrome, safari, mobile safari, firefox and they all work (latest versions of course)
#Alex I see it as more option and more power, programming is art, #Nat share his finding with us, and for that I thank him. Maybe someone want to do it that way.
I'm sure the setter version is the same but just changing that g to a s.
i.g:
function Constructor(input){
this.input = input;
}
Object.__defineGetter__.call(Constructor.prototype, "value", function(){
return this.input * 2;
});
Object.__defineSetter__.call(Constructor.prototype, "bar", function(foo){
return this.input *= foo;
});
var test = new Constructor(5);
console.log(test.value); // 10
test.bar = 5;
console.log(test.input); //25
With that said, this feature is deprecated, advices to not to use in production coding.

How to handle several instance on the same Class in a plugin?

I have a JS plugin using es6 class syntax. I'm not sure on the way to handle several instances of the class versus once instance with a several element inside.
This plugin can have an array an unlimited number of image nodes as parameters.
This is the class syntax I have so far
(function(window) {
function handle(element, options) {
let handles = [];
if (element.length) {
for (var i = 0; i < element.length; i++) {
handles.push(new Plugin(element[i], options));
}
} else {
handles.push(new Plugin(element, options));
}
return handles;
}
class Plugin {
constructor(element, options) {
this.element = element;
this.init();
}
init() {
//get its translated value
this.methodA();
//apply its translation even if not visible for the first init
this.methodB();
}
methodA() {
this.element.classList.add('test');
}
}
return handle;
});
I would like to get rid of this handle function. What is the other way to have an instance of plugin for every element? and to be able to have the classPlugin at the top level without the need for this handle function.
I don't see any other way that having several instances of the class, because each instance get specified info for each image (height, offset, etc). Maybe I am missing something obvious here...
You can't actually instantiate an instance of a class without a loop. You may try eval. But it's not recommended. It's a bad practice.
Now let me explain why it is not possible.
JavaScript does not have classes and instances, it has only objects, which can delegate to other objects.
To create two objects based on a single object, but behind the scenes, there aren’t really two ‘instances’ of the Point object, there are just two objects that delegate to the original one. When you use new, JavaScript is actually just creating an object and setting its prototype to the object returned by the constructor function. Imagine if the example had been expanded to include a shared method like this:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.logCoords = function () {
console.log(this.x, this.y);
};
var a = new Point(1, 2);
console.log(a.x); // logs '1'
a.logCoords(); // logs '1 2'
Behind the scenes, what’s happening is something more like this:
var Point = function (x, y) {
this.x = x;
this.y = y;
};
Point.prototype.logCoords = function () {
console.log(this.x, this.y);
};
var a = {};
a.__proto__ = Point.prototype; // see note below about this
a.constructor = Point;
a.constructor(1, 2);
console.log(a.x); // logs '1'
a.logCoords(); // logs '1 2'

Javascript - Generic Object Function

Ok so my question is how to setup a generic function inside of a Javascript object using the constructor pattern that will take the object instance's data as parameters?
For instance:
function ObjTest() = {
this.obA = 0;
this.obA_Inc = 1;
this.obA_Max = 10;
this.Up_Value = function(ob, obI, obM) {
if(ob <= obM) {
ob += obI;
if(ob > obM) {
ob = obM;
}
}
}
}
var obj = new ObjTest();
obj.Up_Value(obj.obA, obj.obA_Inc, obj.obA_Max)
If there is a better way to work with Javascript objects, that would allow for instances like this, I would love to hear about that pattern.
The way you're talking about these "sets of data", this screams object to me.
Don't use prefixed or enumerated properties to indicate that they belong together. Use a proper Object or an Array. In this case, something like this.objA = {value: 0, inc: 1, max: 10}.
And if this function always operates on one of these objects, and doesn't seem to have any other relationship to ObjTest, then maybe it's not a method of ObjTest, but instead a method of these objects. And would be better off on them.
//So, as these objects have logic, let's use a class
class ValueObj {
constructor(value, inc, max) {
this.value = value;
this.inc = inc;
this.max = max;
}
Up_Value() {
this.value = Math.min(this.value + this.inc, this.max);
}
}
function ObjTest() {
this.objA = new ValueObj(0, 1, 10);
this.objB = new ValueObj(0, 2, 15);
}
var obj = new ObjTest();
//and since you call the method on the object itself,
//you don't need to pass anything to the function
console.log("before", JSON.stringify(obj,null,2));
obj.objA.Up_Value();
console.log("after", JSON.stringify(obj,null,2));
.as-console-wrapper{top:0;max-height:100%!important}
you can use console.log(obj); but in your browser console this won't show the correct "before" state. That's why I use JSON.stringify() in the code.

JS. properties in prototype weird behaviour

I'm trying to understand how to make properties work with prototype in a nodejs backed webgame.
Reasoning: Instead of doing something like:Player.attributes.pow.value
It would be so much easier to read when it's: Player.pow Note: I do not want to use a function because Player.pow() makes you feel like it's doing more than just return a value.
So to test how it works I did a quick mockup and noticed an odd behaviour and although it works not sure if I should be doing this:
function Player() {
this.attributes = {
pow: {
base: 3,
value: 3,
attChanges: [],
multiplier: 0,
calculateValue: function() {
var multiplier = this.multiplier;
var value = 0;
this.attChanges.forEach( function(att) {
value += att.value; // For some reason this.value returns NaN in the forEach, this is a way around that...
multiplier += att.multiplier;
});
this.value = this.base + value;
this.value *= (1 + multiplier / 100);
}
}
}
//Change a attribute and calculate it's value
this.attributes.pow.attChanges.push({value: 3, multiplier: 0});
this.attributes.pow.calculateValue();
}
Player.prototype.sayHello = function() {
console.log("hello");
}
Player.prototype = {
get pow() {
return this.attributes.pow.value;
}
}
var p = new Player();
p.sayHello(); // Error
console.log(p.pow);
console.log(p.pow);
p.sayHello();
It says TypeError: p.sayHello is not a function
But if I put it below defining the property, it works
Player.prototype = {
get pow() {
return this.attributes.pow.value;
}
}
Player.prototype.sayHello = function() {
console.log("hello");
}
var p = new Player();
p.sayHello(); // hello
console.log(p.pow); // 6
console.log(p.pow); // 6
p.sayHello(); // hello
What's going on here? Is this a bad way of doing it? I saw an example of this here: JS defineProperty and prototype It is the second answer at the moment.
When you assign the prototype for the pow instance variable, you are erasing the previous definition for the prototype to which the sayHello method is attached, so when you switch the declarations, the assignment happens first and then you add the instance method to the new prototype so everything works as expected.
If you want to define a property with a get method without redefining the entire prototype object, try something like this:
Object.defineProperty(Player.prototype, "pow", {
get: function() {
return this.attributes.pow.value;
}
});
You may then place that declaration in any order relative to the sayHello declaration without worrying about unexpected side-effects.

Getter/setter in constructor

I recently read about the fact that there is a possibility of defining getters/setters in JavaScript. It seems extremely helpful - the setter is a kind of 'helper' which can parse the value to be set first, before actually setting it.
For example, I currently have this code:
var obj = function(value) {
var test = !!value; // 'test' has to be a boolean
return {
get test() { return test },
set test(value) { test = !!value }
};
};
var instance = new obj(true);
This code always converts value to a boolean. So if you code instance.test = 0, then instance.test === false.
However, for this to work you have to actually return an object, which means that the new instance is not of type obj but just is a plain object. This means that changing the prototype of obj has no effect on instances. For example, this does not work - instance.func is undefined:
obj.prototype.func = function() { console.log(this.value); };
because instance is not of type obj. To get the prototype functions work, I guess I should not return a plain object, but rather not return anything so that instance would just be of type obj, like a regular constructor works.
The problem then is how to implement getters/setters? I can only find articles describing how to add these to an object, not as being part of the constructor of a custom type.
So how do I implement getters/setters in the constructor so as to be able to both use getters/setters and extending the prototype?
You can't do that.
You can set setter/getters for properties of objects though. I advice you use ES5 Object.defineProperties though. of course this only works in modern browsers.
var obj = function() {
...
Object.defineProperties(this, {
"test": {
"get": function() { ... },
"set": function() { ... }
}
});
}
obj.prototype.func = function() { ... }
var o = new obj;
o.test;
o.func();
Usually you want class methods. The answer by #Raynos on May 7, 2011 gets the job done, but it defines an instance method, not a class method.
The following illustrates a class definition with a the getter and setter being part of the class. This definition is a lot like the answer by #Raynos, but with two differences in the code: (1) The "defineProperties()" action has been moved out of the constructor. (2) The argument to "defineProperties()"as been changed from the instance object "this", to the constructor's prototype object.
function TheConstructor(side) {
this.side = side;
}
Object.defineProperties(TheConstructor.prototype, {
area: {
get: function() { return this.side * this.side; }
,set: function(val) { this.side = Math.sqrt(val); }
}
});
// Test code:
var anInstance = new TheConstructor(2);
console.log("initial Area:"+anInstance.area);
anInstance.area = 9;
console.log("modified Area:"+anInstance.area);
Which produces these results:
initial Area:4
modified Area:9
Although usually the distinction between class versus instance
definition is just a matter of style, there is a purpose to
good style, and there is a case where the distinction matters:
the memoized getter. The purpose for a memoized getter is
described here: Smart/self-overwriting/lazy getters
Define the getter at the class level when the memoized value is to
pertain to the entire class. For example, a configuration file
should be read only once; the resulting values should then apply
for the duration of the program. The following sample code
defines a memoized getter at the class level.
function configureMe() {
return 42;
}
Object.defineProperties(TheConstructor.prototype, {
memoizedConfigParam: {
get: function() {
delete TheConstructor.prototype.memoizedConfigParam;
return TheConstructor.prototype.memoizedConfigParam = configureMe();
}
,configurable: true
}
});
// Test code:
console.log("memoizedConfigParam:"+anInstance.memoizedConfigParam);
Produces:
memoizedConfigParam:42
As can be seen in the example, memoized getters have the
characteristic that the getter function deletes itself,
then replaces itself with a simple value that
(presumably) will never change.
Note that 'configurable' must be set to 'true'.
Define the getter at the instance level when the memoized value
depends upon the contents of instance. The definition moves
inside the constructor, and the object of attention is 'this'.
function TheConstructorI(side) {
this.side = side;
Object.defineProperties(this, {
memoizedCalculation: {
get: function() {
delete this.memoizedCalculation;
return this.memoizedCalculation = this.expensiveOperation();
}
,configurable: true
}
});
}
TheConstructorI.prototype.expensiveOperation = function() {
return this.side * this.side * this.side;
}
//Test code:
var instance2 = new TheConstructorI(2);
var instance3 = new TheConstructorI(3);
console.log("memoizedCalculation 2:"+instance2.memoizedCalculation);
console.log("memoizedCalculation 3:"+instance3.memoizedCalculation);
Produces:
memoizedCalculation 2:8
memoizedCalculation 3:27
If you want to guarantee (rather than presume) that the memoized
value will never be changed, the 'writable' attribute needs to
be changed. That makes the code a bit more complicated.
function TheConstructorJ(side) {
this.side = side;
Object.defineProperties(this, {
memoizedCalculation: {
get: function() {
delete this.memoizedCalculation;
Object.defineProperty( this, 'memoizedCalculation'
,{ value : this.expensiveOperation()
,writable : false
});
return this.memoizedCalculation;
}
,configurable: true
}
});
}
TheConstructorJ.prototype.expensiveOperation = function() {
return this.side * this.side * this.side;
}
//Test code:
var instanceJ = new TheConstructorJ(2);
console.log("memoizedCalculation:"+instanceJ.memoizedCalculation);
instanceJ.memoizedCalculation = 42; // results in error
Produces:
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
The OP's original question, from March 7, 2011, presented basic
getter and setter syntax, noted that it worked on an object but
not on 'this', and asked how to define getters and setters within
a constructor. In addition to all the examples above, there is
also a "cheap-shot" way of doing it: create a new object within
the constructor, like the OP did, but then assign the object to
be a member within 'this'. So, the original code would look like
this:
var MyClass = function(value) {
var test = !!value; // 'test' has to be a boolean
this.data = {
get test() { return test },
set test(value) { test = !!value }
};
};
var instance = new MyClass(true);
// But now 'data' is part of the access path
instance.data.test = 0;
console.log(instance.data.test);
Produces:
false
Believe it or not, I have actually run into situations where
this "cheap-shot" is the best solution. Specifically, I used this
technique when I had records from several tables encapsulated within
a single class, and wanted to present a unified view as though
they were a single record called 'data'.
Have fun.
IAM_AL_X
Update for ES6 -- have a look at section 19.3.1 of Alex Rauschmayer's book Exploring ES6 http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-data which demonstrates how to use WeakMaps with getters and setters to hold private data. Combining with section 16.2.2.3 http://exploringjs.com/es6/ch_classes.html#leanpub-auto-getters-and-setters would result in something like
# module test_WeakMap_getter.js
var _MyClassProp = new WeakMap();
class MyClass {
get prop() {
return _MyClassProp.get( this );
}
set prop(value) {
_MyClassProp.set( this, value );
}
}
var mc = new MyClass();
mc.prop = 5 ;
console.log( 'My value is', mc.prop );
$ node --use_strict test_WeakMap_getter.js
My value is 5
function Obj(value){
this.value = !!value;
}
Obj.prototype = {
get test () {
return this.value;``
},
set test (value) {
this.value = !!this.value;
}
};
var obj = new Obj(true);
I know this might be extremely late but I figured out a different way to accomplish what you want and for the sake of people, like myself, googling for an answer to this here it is.
function Constructor(input){
this.input = input;
}
Object.__defineGetter__.call(Constructor.prototype, "value", function(){
return this.input * 2;
});
var test = new Constructor(5);
alert(test.value) // 10
I've tested this in chrome, safari, mobile safari, firefox and they all work (latest versions of course)
#Alex I see it as more option and more power, programming is art, #Nat share his finding with us, and for that I thank him. Maybe someone want to do it that way.
I'm sure the setter version is the same but just changing that g to a s.
i.g:
function Constructor(input){
this.input = input;
}
Object.__defineGetter__.call(Constructor.prototype, "value", function(){
return this.input * 2;
});
Object.__defineSetter__.call(Constructor.prototype, "bar", function(foo){
return this.input *= foo;
});
var test = new Constructor(5);
console.log(test.value); // 10
test.bar = 5;
console.log(test.input); //25
With that said, this feature is deprecated, advices to not to use in production coding.

Categories

Resources