ok weird title, I know. However the question is simple. In a class I want to be able to do these two things:
invoice.getAmount(); // returns 1000
and
invoice.getAmount().asCurrency(); // returns $1000.00
I can do either, just don't know how to get both to work.
What I have for now for the second idea:
getAmount() {
this._temp = this.amount;
return this;
}
asCurrency(){
if(this._temp){
return "$" + this._temp + ".00";
}
}
That's an ugly copy of what I really have but the concept is represented...
Any idea?
Thanks
Trick is to use the valueOf() method.
class Invoice {
constructor(value) {
this.value = value;
}
getAmount() {
return {
valueOf: _ => this.value,
asCurrency: _ => '$' + this.value
}
}
}
const i = new Invoice(150);
console.log(i.getAmount() + 10); // 160
console.log(i.getAmount().asCurrency()); // '$150'
You can use Number.prototype.toLocaleString():
function getAmount() {
return 1000;
}
var result = getAmount().toLocaleString('en-EN', {style: 'currency', currency: 'USD'});
console.log(result);
You can override a few built-ins (toString() and valueOf()) on the Invoice.prototype like so:
function Invoice(amount) {
this.amount = amount;
}
Invoice.prototype.toString =
Invoice.prototype.valueOf = function valueOf() {
return this.value;
};
Invoice.prototype.getAmount = function getAmount() {
this.value = this.amount;
return this;
};
Invoice.prototype.asCurrency = function asCurrency() {
this.value = '$' + this.value.toFixed(2);
return this;
};
var invoice = new Invoice(1000);
console.log(Number(invoice.getAmount()));
console.log(String(invoice.getAmount().asCurrency()));
// or more subtly
console.log(invoice.getAmount() + 0);
console.log(invoice.getAmount().asCurrency() + '');
Or using ES6 class:
class Invoice {
constructor(amount) {
this.amount = amount;
}
toString() {
return this.value;
}
valueOf() {
return this.value;
}
getAmount() {
this.value = this.amount;
return this;
}
asCurrency() {
this.value = '$' + this.value.toFixed(2);
return this;
}
}
var invoice = new Invoice(1000);
console.log(Number(invoice.getAmount()));
console.log(String(invoice.getAmount().asCurrency()));
// or more subtly
console.log(invoice.getAmount() + 0);
console.log(invoice.getAmount().asCurrency() + '');
Remember that getAmount is returning a number.
Thus, there is no way to chain asCurrency on the return value of getAmount (without using valueOf) unless asCurrency exists on the Number prototype.
If you wanted to keep all this composed in your class without modifying the Number prototype, you need to either use valueOf (best solution), or make your return value of getAmount your class instance so that you can chain it with asCurrency.
Related
In this example I would like to call the defineX function before calling the add and value methods
class cl {
#x;
defineX(n) {
this.#x = n;
return this;
}
add(n) {
this.#x += n;
return this;
}
value() {
return this.#x;
}
}
const cObj = new cl();
console.log(cObj.defineX(3).add(5).value()); // output 8
console.log(cObj.add(3)) // should not happen
what is the best solution so I can prevent calling add before defineX method ??
also how to prevent calling the add method after it has been called for the first time ??
Don't use a single class, use multiple ones with different methods, each holding a different immutable state. It's simpler without class syntax at all though:
const cObj = {
defineX(n) {
const x = n;
return {
add(n) {
const v = x + n;
return {
value() {
return v;
}
};
}
};
}
};
console.log(cObj.defineX(3).add(5).value()); // output 8
console.log(cObj.add(3)) // error
With classes:
const cObj = {
defineX(n) {
return new Cl(n);
}
};
class Cl {
#x;
constructor(x) {
this.#x = x;
}
add(n) {
return new Result(this.#x + n);
}
}
class Result {
#v;
constructor(v) {
this.#v = v;
}
value() {
return this.#v;
}
}
Since 1) operations not allowed and 2) breaking from a chain of calls are involved, it seems to me correct way is to throw an exception whenever something wrong happens. You can always try/catch it if you know exception is a possibility.
class cl {
#x;
#defined;
#added;
defineX(n) {
this.#x = n;
this.#defined = true;
return this;
}
add(n) {
if (!this.#defined) {
throw new Error("`add` before define")
}
if (this.#added) {
throw new Error("already did `add`")
}
this.#x += n;
this.#added = true;
return this;
}
value() {
return this.#x;
}
}
const cObj = new cl();
// tests:
// console.log(cObj.defineX(3).add(5).value()); // output 8
// console.log(cObj.add(3)) // should not happen - output error
console.log(cObj.defineX(3).add(5).add(6).value()); // output error
If i have a method-chain that looks something like this:
object.value.add(1).multiply(2);
And the object is this:
var object = {
"value":1,
"add":function(x){
this.value = this.value + x;
return this;
},
"multiply":function(x){
this.value = this.value * x;
return this;
}
}
This would output:
{
"value":4,
"add":function(x){
this.value = this.value + x;
return this;
},
"multiply":function(x){
this.value = this.value * x;
return this;
}
}
But i want it to output:
4
Is this possible?
And i don't want to make another method for the output, i want the "multiply" method (and the "add" method) to multiply the entire object if it's not the last in the method-chain (so that method-chaining is possible), but when it's last i want it to output the "value" attribute.
There's no efficient way (there might not even be a way) for the method to know if it is the last member of the chain.
Why not this?
object.add(1).multiply(2).value
You could also leverage valueOf in very specific scenarios, but it cannot be leveraged as a general purpose strategy to that end.
var object = {
"value":1,
"add":function(x){
this.value = this.value + x;
return this;
},
"multiply":function(x){
this.value = this.value * x;
return this;
},
valueOf: function () { return this.value; }
};
console.log(object.add(4).multiply(2) / 2); //5
I ran into the problem of the value misplacement in the constructor function method this.result. I do not understand why I'm get the result of the end of the function - undefined...
Tell me please, what is forgotten to include in the function :(
function Accumulator(startingValue) {
this.startingValue = startingValue;
this.read = function() {
this.a = +prompt('Your digit: ', '');
};
this.value = function() {
this.value += this.a;
};
this.result = function() {
return this.value + this.startingValue;
}
}
var accumulator = new Accumulator(1); // starting value 1
accumulator.read(); // sum prompt with current value
accumulator.read(); // sum current prompt with previous prompt and current value
console.log( accumulator.result() ); // display sum result
If .value is supposed to be an integer, don't define it as a function :-)
I think you should drop .value(), .startingValue and .a and just use .value everywhere. Put the summation directly into the read method. Simplify to:
function Accumulator(startingValue) {
this.value = startingValue;
this.read = function() {
// the temporary variable might be unnecessary but I left it in for verbosity
const a = +prompt('Your digit: ', '');
this.value += a;
};
this.result = function() {
return this.value;
};
}
var accumulator = new Accumulator(1); // starting value 1
accumulator.read(); // add prompt to current value
accumulator.read(); // add another prompt to current value
console.log( accumulator.result() ); // display sum by calling result() method
You might also want to define the methods on the prototype:
function Accumulator(startingValue) {
this.value = startingValue;
}
Accumulator.prototype.read = function() {
this.value += +prompt('Your digit: ', '');
};
Accumulator.prototype.result = function() {
return this.value;
};
and even use modern class syntax, as #ArtificialBug suggested:
class Accumulator {
constructor(startingValue) {
this.value = startingValue;
}
read() {
this.value += parseInt(prompt('Your digit: ', ''), 10);
}
result() {
return this.value;
}
}
There are two problems
this.value = function() {
this.value += this.a; //this.value is a function
};
and
console.log( accumulator.value ); // accumulator value is a function which needs to be invoked
Make it
function Accumulator(startingValue) {
this.startingValue = startingValue;
this.read = function() {
this.a = (this.a || this.startingValue ) + +prompt('Your digit: ', '');//initialize and add the prompted value
};
this.value = function() {
return this.a; //simply return the value
};
this.result = function() {
return this.a + this.startingValue; //this.a instead of this.value
}
}
var accumulator = new Accumulator(1);
accumulator.read();
accumulator.read();
console.log( accumulator.value() ); // invoke the method
An example from some javascript course that I am following:
var Tornado = function(name, cities, degree) {
this.name = name;
this.cities = cities;
this.degree = degree;
};
Tornado.prototype = {
nCities: function() {
return this.cities.length
},
valueOf: function() {
return this.nCities() * this.degree;
},
toString: function() {
return this.cities[0][0].toString() + " " + this.name;
}
}
cities = [["Washington", 1], ["Rotterdam", 2]]
var x = new Tornado("crazy", cities, 3)
console.log(x.nCities())
console.log(x.valueOf())
console.log(x + 16)
console.log(x.toString() + "... wow!")
Object.prototype.findOwnerOfProperty = function(propName) {
var currentObject = this;
while(currentObject !== null) {
if(currentObject.hasOwnProperty(propName)) {
return currentObject;
} else {
currentObject = currentObject.__proto__;
}
}
return "No property found!";
};
console.log(x.findOwnerOfProperty("toString"));
The findOwnerOfProperty function returns the object where the property is defined. This is nice, but it would be nicer to also have the name of that object (Tornado.prototype in this example), how can I do that?
No built-in solution. but you can make a property
this._constructor = arguments.callee.name;
inside Tornado function and make a
getConstructor:function(){
return this._constructor;
}
inside prototype.
BTW , forgot to mention that you should remake
var Tornado = function
to:
function Tornado
I have a problem with return a value from an object.
my object looks like this.
function XYZ(date, startT)
{
var _date=date;
var _startT=startT;
this.get_date = function() {
return _date;
};
this.set_date = function(value) {
_date=value;
};
this.get_startT = function() {
return _startT;
};
this.set_startT = function(value) {
_startT=value;
};
this.toString()
return (_date + " " _startT);
}
then i create an Array like this
jsData[0] =new XYZ("2012-11-11","8:00");
jsData[1] = new XYZ("2012-03-03","8:00");
when i want to use get_date method it didn't return me the value but the get_startT method works fine.
When i show object with .toString method it also show me full object
Please help.
It works if you fix all the syntax errors:
function XYZ(date, startT) {
var _date=date;
var _startT=startT;
this.get_date = function() {
return _date;
};
this.set_date = function(value) {
_date=value;
};
this.get_startT = function() {
return _startT;
};
this.set_startT = function(value) {
_startT=value;
};
}
var jsData = [];
jsData[0] = new XYZ("2012-11-11","8:00");
jsData[1] = new XYZ("2012-03-03","8:00");
display("jsData[0].get_date() = " + jsData[0].get_date());
Output:
jsData[0].get_date() = 2012-11-11
Live Copy | Source
Other than obvious typos, here's what I did:
Put { and } around the function body.
Removed the this.toString() which was non-functional (a no-op, as you didn't store the result anywhere).
Removed the return at the end, because returning a string primitive out of a constructor function is another no-op.
Declared jsData.
Initialized jsData.
You appear to be missing a opening bracket { after
function XYZ(date, startT)
And one at the end of your code. (})
Try adding methods to the function prototype like this:
function XYZ(date, startT) {
this._date = date;
this._startT = startT;
}
XYZ.prototype.get_date = function() {
return this._date;
}
XYZ.prototype.set_date = function(value) {
this._date = value;
}
XYZ.prototype.get_startT = function() {
return this._startT;
}
XYZ.prototype.set_startT = function(value) {
this._startT = value;
}
XYZ.prototype.toString = function() {
return this._date + " " + this._startT;
}
var myXYZ = new XYZ("2012-11-11","8:00");
myXYZ.toString(); // "2012-11-11 8:00"
I tested that in the console and it outputs the final string correctly.