Getter and setter without members - javascript

Can we use getters and setters without defining a method for a member?
For example, transform this
class int {
set value(val) {
this._value = val | 0; // Truncate
}
get value() {
return this._value;
}
}
var x = new int();
x.value = 5 / 2;
console.log(x.value); // shows 2 instead of 2.5
to something like this:
class int {
set (val) {
this = val | 0; // Truncate
}
get () {
return this;
}
}
var x = new int();
x = 5 / 2;
console.log(x); // shows 2 instead of 2.5

There's no operation you can tap into for when the value of a variable (x in your case) is replaced with a new value. That's just not something JavaScript has. You can't do that even with a Proxy.
Your first definition of int is probably about as close as you're going to get.
People have tried various ways of getting primitive-like things like your int. None of them is really satisfactory. For instance, this is a not-uncommon attempt:
class Int {
constructor(value) {
Object.defineProperty(this, "value", {
value: value | 0,
enumerable: true
});
}
set(value) {
return new this.constructor[Symbol.species](value);
}
valueOf() {
return this.value;
}
toString() {
return this.value; // Even though it's not a string
}
static get [Symbol.species]() {
return this;
}
}
then:
let n = new Int(5);
console.log(`n = ${n}`); // n = 5
n = n.set(n / 2);
console.log(`n = ${n}`); // n = 2
but as soon as you do something that doesn't coerce to a primitive, like:
console.log(n);
you see the object-ness of it. You have to do:
console.log(+n);
which makes it a pretty big footgun, though the immutability helps with things like let m = n..
Example:
class Int {
constructor(value) {
Object.defineProperty(this, "value", {
value: value | 0,
enumerable: true
});
}
set(value) {
return new this.constructor[Symbol.species](value);
}
valueOf() {
return this.value;
}
toString() {
return this.value; // Even though it's not a string
}
static get [Symbol.species]() {
return this;
}
}
let n = new Int(5);
console.log(`n = ${n}`); // n = 5
n = n.set(n / 2);
console.log(`n = ${n}`); // n = 2
// But
console.log(n); // (object representation of it)

Related

Javascript Create new Number wıth own properties

I want create new number object with own properties from Number.
But when assign a number value to my variable, my variable turn to Number(primitive wrapper object).and loss own properties.How can I prevent this?
Example
class Fnumber extends Number {
value = 0;
[Symbol.toPrimitive](hint) {
if (hint === 'object') {
return this;
}
return this.value;
};
//I don't want use Number.prototype.add method because this effect all Number values.
add = function(...v) {
this.value += Array.prototype.slice.call(arguments).reduce((o, v) => o + v)
}
}
var nmbr = new Fnumber();
nmbr.add(4, 2, 4);
console.log(nmbr); //return a Object
console.log(nmbr + 4); //return 14 is number
nmbr = 14;
console.log(nmbr); //return not a Object its Primative number value
console.log(nmbr + 4); //return 14 a number
nmbr.add(4, 2, 4); //raise error.
When doing nmbr = 14 you assign 14 to nmbr, you are not changing the nmbr.value, you are overwriting the object. Instead call add and use nmbr.value when needed.
class Fnumber extends Number {
value = 0;
add(...v) {
this.value += Array.prototype.slice.call(arguments).reduce((o, v) => o + v)
}
}
var nmbr = new Fnumber();
nmbr.add(4, 2, 4);
console.log(nmbr.value);
nmbr.add(5);
console.log(nmbr.value);
nmbr.value = 25; // assign a value to nmbr.value
console.log(nmbr.value);
If you are not planning on reassigning the object, a good practice is to use const instead of var, see the error below.
class Fnumber extends Number {};
const nmbr = new Fnumber();
nmbr = 14;
I found the solution indirectly.
class Fnumber extends Number {
value = 0;
add(...v) {
this.value += Array.prototype.slice.call(arguments).reduce((o, v) => o + v)
}
}
//temğrary variable description
var _nmbr=new Fnumber();
//Define nmbr variable in global scobe
Object.defineProperty(window,"nmbr",{
enumerable: true,
configurable: true,
get() { return _nmbr; },
set(val) { if (typeof val=="number")_nmbr.value=val; }
});
nmbr=4;
console.log(nmbr+2);//output 6
//still nmbr variable is object,because assigned value to
_nmbr.value with above set method
nmbr.add(4, 2, 4);
console.log(nmbr+2);//output 16

Can I create a class method usable by each property of an created object?

Can I create a method usable by each property of an object?
class myClass {
constructor() {
this.a = 1;
this.b = 2;
this.c = 3;
}
raise(x) {
this += x; //I know this doesn't work. I want the raise function to use
//the value of its caller and increase it by x
};
}
What I want to achieve is being able to call the raise method on any object property via
obj1 = new myClass();
obj1.a.raise(1); //a = 2
obj1.b.raise(3); //b = 5
obj1.c.raise(100); //c = 103
So I want to be able to use this syntax: object.property.method()
I tried creating raise on the class constructor, the class prototype or the Object prototype. I also couldn't find any mention of this elsewhere.
It would be impossible for obj.a to return a number, while also being able to call obj.a.raise to modify it.
If you're open to a small tweak, you could retrieve the value via a method called something like .get, while the underlying value is stored and reassigned in the _a property:
class myClass {
constructor() {
this._a = 1;
this.a = { raise: num => this._a += num, get: () => this._a };
}
}
const obj = new myClass();
obj.a.raise(1); //a = 2
console.log(obj.a.get());
Using ES6 you can do it easily. You may not need complex solution!
Sample is given below.
2nd Solution, to use composition.
class PriceList {
constructor(price, amount) {
this._price = price;
this._amount = amount;
}
get price() {
return this._price;
}
set price(p) {
this._price += p; // validation could be checked here such as only allowing non numerical values
}
get amount() {
return this._amount;
}
set amount(p) {
this._amount += p; // validation could be checked here such as only allowing non numerical values
}
}
const priceList = new PriceList(1, 10);
console.log(priceList); // PriceList { _price: 1, _amount: 10 }
priceList.amount = 10;
priceList.price = 100;
console.log(priceList); // PriceList { _price: 101, _amount: 20 }
class Value {
constructor(v) {
this._val = v;
}
get val() {
return this._val;
}
set(v) {
this._val = v;
}
raise(m) {
this._val += m;
}
}
class PriceList2 {
constructor(price, amount) {
this.amount = new Value(amount);
this.price = new Value(price);
}
}
const priceList2 = new PriceList2(1, 10);
console.log(priceList2);
priceList2.amount.raise(10);
priceList2.price.raise(100);
console.log(priceList2.amount.val); // 20
console.log(priceList2.price.val); // 101
priceList2.amount.val = 1
priceList2.amount.raise(10);
console.log(priceList2.amount.val); //30
.as-console-row {color: blue!important}
For literally all answers (except this one), you need to run a function to get the value, here it's more simplified.
class myClass {
constructor() {
this.a = new Number(1);
this.b = new Number(2);
this.c = new Number(3);
for (let p in this) {
this[p].raise = x => {
let fn = this[p].raise;
this[p] = new Number(x + this[p]);
this[p].raise = fn;
}
}
}
}
let m = new myClass();
m.a.raise(15);
m.a.raise(15);
// A = 1 + 15 + 15 = 31
m.b.raise(10);
m.b.raise(20);
// B = 2 + 10 + 20 = 32
m.c.raise(17);
m.c.raise(13);
// C = 3 + 17 + 13 = 33
console.log(m.a + 1, m.b + 1, m.c + 1) // 32 33 34
// logging just m.a, m.b, m.c will get you an object with a raise function
// only in stack overflow (or maybe other fake consoles), but the browser
// console will definitely work and give you a Number object

How to add chain functions from one class to another?

I am trying to develop a game engine for personal use in JS. I want to have my engine be able to use elements of separate classes. One such problem I am trying to solve is chaining methods from one class (say a math class that chains functions) to my main function.
Here is an example of what I want it to look like:
let game = new Engine()
game.operator(5).sum(3).divide(2)
Here's what it might be in my code, although this is where I am not sure what to do.
class Engine {
constructor() {
//Set up engine here
/* This is where I am unsure how to link the following operator class to this one.
* Do I put it here in constructor or ...
*/
}
/* ... do I put it here? (Or not in this class at all?)
*
* This is what I tried doing as a function
*
* operator(input) {
* let op = new Operator(input);
* }
*/
}
class Operator {
/*The following class is different than the one I am using, but follows similar syntax:
* Basic usage: operator(5).sum(3, 4) => Should output 12
* How the class works is mostly irrelevant, just know it chains methods.
*/
constructor(input) {
this.input = input;
this.answer = this.input;
}
sum() {
let a = arguments[0]
for(var i = 1; i < arguments.length; i++) {
a += arguments[i];
}
this.answer += a;
return this;
}
divide() {
let a = arguments[0];
for(var i = 1; i < arguments.length; i++) {
a *= arguments[i];
}
this.answer /= a;
return this;
}
}
How can I get one class to be able to chain methods from different class?
A pattern for chaining is to have the instance keep a chain state, and to provide a 'value' method that returns the chain state. To chain between two classes, I guess I'd include a special value method that returns an instance of the other class. (To keep the reader oriented, name it something that indicates the type change)...
class ObjectA {
constructor(string) {
this.chainValue = string
this.string = string
}
transformA() {
this.chainValue = this.chainValue.toUpperCase()
return this
}
transformB() {
this.chainValue = this.chainValue + "bar"
return this
}
// the regular value method
get value() {
return this.chainValue
}
// like the value method, but named to explicitly return MyNumber
get numberValue() {
return new MyNumber(this.value.length)
}
}
class MyNumber {
constructor(int) {
this.chainValue = int
this.int = int
}
add(n) {
this.chainValue += n
return this
}
get value() {
return this.chainValue
}
}
let a = new ObjectA("foo")
console.log(
a
.transformB() // append "bar"
.transformA() // convert to upper case
.numberValue // return a number (arbitrarily, the length of the chain state)
.add(12) // add 12
.value // expect 18
)
You can use Proxy for that purpose.
class Engine {
operator() {
// code
console.log('call operator')
}
}
class Operator {
sum() {
// code
console.log('call sum')
}
divide() {
console.log('call divide')
}
}
class SuperOperator {
negate() {
console.log('call negate')
}
}
const operator = new Operator();
const superOperator = new SuperOperator();
const engine = new Engine();
const objectsToChainFrom = [
engine,
operator,
superOperator,
];
// create helper function for proxy
function wrapper(originalMethod, ctx) {
return function () {
originalMethod.apply(ctx, arguments);
// return proxy;
return this;
}
}
var proxy1 = new Proxy(objectsToChainFrom, {
get(target, methodToCall, receiver) {
const objectWithMethod = target.find(el => el[methodToCall]);
return wrapper(objectWithMethod[methodToCall], objectWithMethod)
}
});
proxy1
.sum()
.operator()
.divide()
.negate()

Best way to implement Javascript chaining in a library

I'm creating a JavaScript library. I've been trying to implement chaining.
0: What I first came up with:
function V(p) {
return {
add : function(addend) { return V(p + addend); },
sub : function(subtra) { return V(p - subtra); },
};
}
Using this method I can chain easily:
V(3).add(7).sub(5) // V(5)
Unfortunately the result is always a wrapped V() function, I am unable to extract the resulting value this way. So I thought about this problem a bit and came up with two semi-solutions.
1: Passing flag to last method
function V(p, flag) {
if(flag)
return p;
else
return {
add : function(addend, flag) { return V(p + addend, flag); },
sub : function(subtra, flag) { return V(p - subtra, flag); }
};
}
Using this method I can end the chain by passing a flag to the last method I use:
V(3).add(7).sub(5, true) // 5
While this works just fine, it requires some code repetition and makes chaining less readable and my code less elegant.
2: Using start() and end() methods
_chain = false;
function V(p) {
function Wrap(w) {
return (_chain) ? V(w) : w;
}
return {
add : function(addend) { return Wrap(p + addend); },
sub : function(subtra) { return Wrap(p - subtra); },
start : function() { _chain = true; },
end : function() { _chain = false; return p; }
};
}
Using this method you can do single operations with no more code:
V(3).add(7) // 10
But chaining requires two more methods, making things a lot less readable:
V(3).start().add(7).sub(5).end() // 5
So basically I'm just searching for the best way to implement chaining into my library. Ideally I'm looking for something where I can use any number of methods and don't need to terminate the chain in inelegant ways.
V(3).add(7).sub(5) // 5, perfect chaining
Why not introducing a private variable and work on that? I guess that is even more convenient. Plus it's probably a good idea to have a pure "getter" that finally returns the computed value. This could look like this:
function V(p) {
var value = p;
return {
add: function(addend) {
value += addend;
return this;
},
sub: function(subtra) {
value -= subtra;
return this;
},
get: function() {
return value;
}
};
}
console.log(V(3).add(7).sub(5).get()); // 5
You cannot return the Object in a getter function obviously. So you need some method where the chaining ends and returns a value.
In some cases it does need to have something similar to end, but in your simple arithmetic example, it does not.
function V(initial_val){
if(!(this instanceof V)){
return new V(initial_val);
}
var num = initial_val || 0;
this.set = function(val){
num = val;
return this;
}
this.add = function(val){
num += val;
return this;
}
this.sub = function(val){
num -= val;
return this;
}
this.valueOf = function(){
return num;
}
this.toString = function(){
return ""+num;
}
}
By adding valueOf and toString functions to the object, you can access its primitive value. That is, you can do something like:
var num = V(0).add(1).sub(2), another_num = 3 + num; // num = -1 and another_num = 2;
I would amend Haochi's excellent answer as follows :
Using the prototype will be more efficient if you have many V objects and
in the toString function I invoke the generic number toString with whatever
arguments you care to give it.
function V (n) {
if (!(this instanceof V)) {
return new V (n);
}
this.num = +n || 0;
return this;
}
V.prototype = {
set: function (val) {
this.num = val;
return this;
},
add: function (val) {
this.num += val;
return this;
},
sub: function (val) {
this.num -= val;
return this;
},
valueOf: function () {
return this.num;
},
toString: function () {
return this.num.toString.apply (this.num, arguments);
}
}

Javascript enum within an enum

I have the following "Enum" in javascript to indicate the state of my application:
var State = {
STATE_A: 0,
STATE_B: 1,
STATE_C: 2
//...
}
Now, I want each state to have a "sub-state". So for example, STATE_B could be in STATE_B1 or STATE_B2 ...
What would be the best way to structure this? Would I somehow nest an "enum" within the State "enum" ? Thanks
If there is a better way to structure this altogether (other than enums) I'm all ears. Basically I need to be able to set and check the state of my application, and each state can (but not necessary) have a sub-state attached to it which can be set and checked. Even better if the solution allows me to go more than 1 level of nesting deep.
What you're doing isn't really enums. You're using native Javascript objects and just treating them like enums, which is perfectly acceptable when you want an enum-like object in Javascript.
To answer your question, yes, you can totally nest objects:
var State = {
STATE_A: 0,
STATE_B:{
SUBSTATE_1 : "active",
SUBSTATE_2 : "inactive"
},
STATE_C: 2
//...
}
You then just use the dot notation in order to set those values, like
State.State_B.SUBSTATE_2 = "active".
You could use some sort of bit-field if you want:
var State = function() {
// Note this must increment in powers of 2.
var subStates = 8;
var A = 1 << subStates;
var B = 2 << subStates;
var C = 4 << subStates;
var D = 8 << subStates;
return {
// A
// Note this must increment in powers of 2.
STATE_A: A,
STATE_A1: A | 1,
STATE_A2: A | 2,
STATE_A3: A | 4,
STATE_A4: A | 8,
STATE_A5: A | 16
// B
STATE_B: B,
STATE_B1: B | 1,
// C
STATE_C: C,
STATE_C1: C | 1,
STATE_C2: C | 2,
STATE_C3: C | 4,
STATE_C4: C | 8,
// D
STATE_D: D
};
}();
// Set a state.
var x = State.STATE_A1; // Same as State.STATE_A | State.STATE_A1
// Determine if x has root state of A?
if(x & State.STATE_A == State.STATE_A) {
console.log("Root state is A.");
}
else {
console.log("Nope, not root state A.");
}
// Determine if x has sub-state A1?
if(x & State.STATE_A1 == State.STATE_A1) {
console.log("A with Substate 1");
}
So the first 8 bits are reserved for setting the sub-state. You could, of course, increase this as long as the root-state and sub-state can fit inside a 32-bit integer. If you need explanation as to why/how this works (bit-wise operators), let me know.
I guess you want to write something like
if (State.STATE_A === someState) { ... }
You could simply define another layer in your State object like
var State = {
STATE_A : 0
STATE_B : {
B1 : 1,
B2 : 2,
}
};
...
if (State.STATE_B.B1 == someState){...}
Edit: Based on the comments on your question another approach could be this.
//Creates state objects from you json.
function createStates(json) {
var result = {};
for(var key in json) {
result[key] = new State(json[key]);
}
return result;
}
//State class
function State(value) {
//If the state value is an atomic type, we can do a simple comparison.
if (typeof value !== "object") {
this.value = value;
this.check = function(comp){ return value === comp; };
}
// Or else we have more substates and need to check all substates
else if (typeof value === "object") {
this.value = createStates(value);
for(var key in this.value) {
//Allows to access StateA.SubStateA1. Could really mess things up :(
this[key] = this.value[key];
}
this.check = function(comp){
for(var key in this.value) {
if (this.value[key].check(comp) === true){
return true;
}
}
return false;
};
}
};
Now you can call everything with
var stateJson = {
STATE_A : 0,
STATE_B : {
B1 : 1,
B2 : 2
}
};
var states = createStates(stateJson);
alert(states.stateA.check(0)); // Should give true
alert(states.STATE_B.B1.check(1)); // Same here
alert(states.STATE_B.check(1)); //And again because value is valid for one of the substates.
Since JavaScript does not support operator overloading, you cannot directly test for equality of substates using the == operator. The closest you can get is to use the instanceof operator to check if a state is of a given type, for example:
// All these functions are empty because we only need the type and there is no data
function State() {
}
function State_A() {
}
State_A.prototype = new State();
function State_B() {
}
State_B.prototype = new State();
function State_B1() {
}
State_B1.prototype = new State_B();
function State_B2() {
}
State_B2.prototype = new State_B();
And since functions are also objects, you can add your nesting right into the State function:
State.STATE_A = new State_A();
State.STATE_B = new State_B();
State.STATE_B.STATE_B1 = new State_B1();
State.STATE_B.STATE_B2 = new State_B2();
And check its type:
var myState = State.STATE_B1;
myState instanceof State // true
myState instanceof State_A // false
myState instanceof State_B // true
myState instanceof State_B1 // true
function State () {
this.superState = null;
}
State.prototype = {
constructor: State
, mkSubState () {
var subState = new State ();
subState.superState = this;
return subState;
}
, isSubStateOf (superState) {
var state = this;
while (state !== null) {
if (this.superState === superState) {
return true;
}
state = this.superState;
}
return false;
}
, isSuperStateOf (subState) {
while (subState !== null) {
if (subState.superState === this) {
return true;
}
subState = subState.superState;
}
return false;
}
};
var States = {};
States.A = new State ();
States.A1 = States.A.mkSubState ();
States.A2 = States.A1.mkSubState ();
States.B = new State ();
States.B1 = States.B.mkSubState ();
States.B2 = States.B1.mkSubState ();
States.B2.isSubStateOf (B); // true
States.B2.isSubStateOf (B1); // true
States.B2.isSubStateOf (B2); // false
States.B2.isSubStateOf (A); // false
States.B2.isSubStateOf (A1); // false
States.B2.isSubStateOf (A2); // false

Categories

Resources