Here is what I'm trying to do. I'm trying to pass an instance of order to bill, where it would be indexed. The thing is that it's not working.
Am I stretching JS too thin here?
Any example on how to do this, or some reading material?
EDIT: Maybe I should add that this is supposed to be the user interface for a POS (Point of Sale) system. It should accept the order of products (each one with variable quantity), and process in the client's side the subtotal, total and number of items in the bill.
EDIT2: Not native english speaker. Maybe the names that I choose did not best suited the problem.
function Bill (prefix,maxForms,minForms) {
this.prefix = prefix;
this.maxForms = maxForms;
this.minForms = minForms;
this.items = [];
this.total = 0;
this.addOrder = function(order) {
if (this.items.length == 0)
{
this.items.push(order);
}
for (i=0;i<this.items.length;i++){
if (this.items[i].name === order.name) {
this.items[i].quantity = order.quantity;
this.items[i].price = order.price;
}
else {
this.items.push(order);
}
this.total = this.total + order.getSubTotal();
}
}
}
function Order (name,price,quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
this.getSubtotal = function () {
return this.price*this.quantity;
}
this.changeQuantity = function (newQuantity) {
this.quantity = newQuantity;
}
this.incrementQuantity = function () {
this.quantity = this.quantity + 1;
}
}
Here's an issue:
for (i = 0;/*...*/)
I would suggest you spend a little more time in JS.
It does look a lot like C / Java / C# / PHP, etc...
The problem, however, is that JS does not have any notion of block scope*.
* until ES6, that is
It only deals with function scope.
That is, a variable has the same reference through the whole function where it's defined (via var).
If a variable is not defined via var, the function goes up to its parent to find the value of the variable, and up from there, and up from there, until it hits window.<varname>.
You might actually be modifying window.i in your class' instance.
function Bill ( ) {
var bill = this,
i = 0;
for (i=0; /* ... */) { /*...*/ }
}
That said, you might do to spend time getting to know JS.
Most of what you've written looks absolutely fine, in English, as well.
I might break it down a little further:
function Bill () {
var bill = this;
extend(bill, {
total : 0,
items : [],
addOrder : function (order) {
var match = bill.findOrder(order.name);
if (!match) { bill.items.push(order); }
else { bill.updateOrder(match, order); }
bill.updateTotal();
},
findOrder : function (name) {
var matches = bill.items.filter(function (order) {
return order.name === name;
});
return matches[0];
},
updateOrder : function (current, updated) {
/* I don't know if you want to replace the old order, or add to it... */
/* so I'm "replacing" it, instead of increasing quantity, like you did */
current.quantity = updated.quantity;
current.price = updated.price;
},
updateTotal : function () {
bill.total = bill.items
.map(function (order) { return order.getSubtotal(); })
.reduce(function (tally, price) { return tally + price; }, 0);
}
});
}
var bill = new Bill();
bill.addOrder(new Order(/*...*/));
I'm doing a few things differently, here.
First, extend isn't a "built-in" function; there are a lot of implementations, in all sorts of libraries, but basically, it just saves me from writing bill.x = x; bill.y = y; bill.z = z;..., and use an object, instead.
Next, I'm using var bill = this;
and bill.method = function () { bill.total = /*...*/; };
instead of this.method = function () { };, because once you go two levels down, in functions, this no longer means the object you think it does.
this.method = function () {
this.async(function (response) {
// unless you change it yourself, `this` probably means `window`
this.value = response; // oops
});
};
// instead, try
var thing = this;
thing.method = function () {
thing.async(function (response) {
thing.value = response;
});
};
Of course, you can always mix and match, as long as you know how far down you can go (one level)...
...but that means you really, really need to care about using this a whole lot.
var thing = this;
this.method = function () {
this.async(function (val) {
thing.value = val;
});
};
Much more confusing than just referring to the instance by a variable, rather than combining the two.
There are dozens of ways of doing this; some look very class-like, others might be 100% functional, and in ES6, you might just use classes altogether.
But there are some ideas, and some reasons behind doing them that way (especially if you don't know where the differences are in JS vs the other C-looking languages).
I don't think you're stretching JS too thin, at all.
Once all of the issues on line 80 are fixed. All you need to do is:
var order = new Order("My Order", 12, 2);
var bill = new Bill(blah, blah, blah);
bill.addOrder(order);
A few issues right off the bat:
this.total = this.total + order.subTotal();ยท
There is a garbage char at the end.
Order does not have a subtotal function. It should be getSubtotal.
The 2 assignments to this.items[i].quantity and this.items[i].price are superfluous, since you are assigning properties to themselves. Remember, this.items[i] === order. This is not a bug, but it is inefficient.
You should have something like this.total = 0; at the top of Bill.
I think you want:
this.items[i].quantity += order.quantity;
this.items[i].price += order.price;
This will update quantity with whatever quantity order has. Secondly, I see you have an order function. Not an order object. Was that intentional? Are you planning to add instances of this bill/order object to each other? I don't think that's where you were going. Make sure they are separate objects that you are nesting.
Are you getting anything except undefined? I don't think you are because you're not returning anything.
Put:
return this;
at the end of your functions. Make sure you save them to a var when you make them:
bill = Bill(v,v,v);
order = Order(v,v,v);
then you can:
bill.addOrder(order);
See if that helps.
Related
If I have a 2-way relationship between 2 objects, such that A relates to B and B relates to A, how can I keep this consistent such that the 2 objects will always refer back to each other?
I'm struggling to put my very simple problem into words, so here's a very simple example. I start with Husband and Wife:
function Husband() { this.wife; }
function Wife() { this.husband; }
var harry = new Husband();
var wendy = new Wife();
harry.wife = wendy;
wendy.husband = harry;
Logically, if Harry's wife is Wendy, then Wendy's husband is Harry.
I need a way of keeping this relationship consistent. So I create a setter method on Husband and denote that the wife variable should be treated as private by prefixing with an underscore.
function Husband() {
this._wife;
this.setWife = function(wife) {
this._wife = wife;
wife.husband = this;
}
}
Describing this relationship is now simple and encourages consistency:
harry.setWife(wendy);
Likewise, it would be nice to have the reverse option:
wendy.setHusband(harry);
To do this I create a setHusband method on Wife, and tweak Husband as needed.
function Husband() {
this._wife;
this.setWife = function(wife) {
this._wife = wife;
// wife.husband = this; // <-- husband is now _husband (private)...
wife.setHusband(this); // <-- switching to a public method
}
}
function Wife() {
this._husband;
this.setHusband = function(husband) {
this._husband = husband;
husband._wife = this; // <-- Oops! _wife is private!
husband.setWife(this); // <-- Oops! this results in an infinite loop!
}
}
At this point I run into a snag. My new setHusband method needs to be able to maintain consistency, but wife is now _wife (private), and calling setWife causes an infinite loop because they reciprocate each other.
I could create another set of methods like reallyJustSetHusband but that seems silly.
My conundrum isn't particular to JavaScript, but I've mentioned it in the question in case there's a particular approach needed.
What is the best way to achieve consistency between these 2 objects? Is there something I've overlooked?
Similar Pattern in the DOM
In the DOM, if you call parent.appendChild(child), then child.parentNode === parent. They are never inconsistent. If a parent has a child the child has the same parent. Other relationships such as nextSibling are also kept consistent.
A simple way is to just check for redundant values and abort early:
function Husband() {
this._wife;
this.setWife = function(wife) {
if(this._wife == wife) return; // prevents recursion
this._wife = wife;
wife.setHusband(this);
}
}
function Wife() {
this._husband;
this.setHusband = function(husband) {
if(this._husband == husband) return; // prevents recursion
this._husband = husband;
husband.setWife(this);
}
}
you could also use an external state manager (redux, sql, etc) with update events, or have direct properties that don't need setters and be careful about keeping the data updated.
Something like this is one way to do what you're asking:
function Husband() {
this.marry = function(wife) {
this.wife = wife;
wife.husband = this;
}
this.say = function() {
if (this.wife) {
console.log('Hi! I\'m married to a wife!');
} else {
console.log('Hi! I\'m single, no wife.');
}
}
}
function Wife() {
this.marry = function(husband) {
this.husband = husband;
husband.wife = this;
}
this.say = function() {
if (this.husband) {
console.log('Hi! I\'m married to a husband!');
} else {
console.log('Hi! I\'m single. No husband.');
}
}
}
var h = new Husband();
var w = new Wife();
h.marry(w);
w.say();
h.say();
._wife isn't necessarily private - a wife knows its husband and should be allowed to set his properties. Also you can easily break out of the infinite loop:
class Wife() {
constructor() {
this._husband=null;
}
get husband() { return this._husband }
set husband(h) {
if (this._husband) this._husband._wife = null;
this._husband = h;
if (h && h.wife != this) h.wife = this;
}
}
If you don't want this insider knowledge, you need to use an intermediate class Marriage that manages a relationship (can be created, queried and broken).
class Marriage {
constructor(h, w) {
this.husband = h;
this.wife = w;
}
divorce() {
this.husband.marriage = null;
this.wife.marriage = null;
}
}
class Wife() {
constructor() {
this.marriage = null;
}
get husband() {
return this.marriage && this.marriage.husband;
}
set husband(h) {
if (this.marriage) this.marriage.divorce();
if (h.marriage) h.marriage.divorce();
this.marriage = h.marriage = new Marriage(h, this);
}
}
I will give you a sample example of my problem to remove the logical complexity and let you be focus on the important part. Of course, this example will be a bit useless...
I have a tree structure where node are like that
{
path: "...",
childs : []
}
Now, I have to write all the full paths from root to each leaf in an array.
My design is very poor:
function listPaths(node) {
var result = [];
function listForNode(n, parentFullPath) {
var thisPath = parentFullPath + "/" + n.path;
result.push(thisPath);
n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
listForNode(node, "");
return result;
}
It could be nice but I can't write the test with Mocha without having an insane 600 line code test file. At this moment, you should be asking why. The reason is the complexity of the real purpose, that's not relevant for my question. My goal is to having something 'mockable' cause I'm used to. (Java dev). But I fail.
Do you have any pattern that I can use to resolve this one? I'm not really good at JS patterns. :/
Visitor? Making an Y Combinator? So many possibility...
Thank you for reading me
You need to remember that functions are first class citizens in javascript.
I see that essentially what you have is something like
function createVisitor(parentsAccumulatorInitialValue, parentsAccumulator){
var visitor = function myVisitor (node) {
var result;
function listForNode(n, parentsAcc) {
var thisPath = parentsAccumulator(parentsAcc, n);
result.push(thisPath);
n.childs && n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
result = [];
listForNode(node, parentsAccumulatorInitialValue());
return result;
}
return visitor;
}
var listPaths = createVisitor(
function parentInit () {
return "";
},
function parentAcc (parentFullPath, n) {
return parentFullPath + "/" + n.path;
});
But that's not the only abstraction you could take care of:
function createVisitor2(
totalAccumulatorInitialValue,
totalAccumulator,
parentsAccumulatorInitialValue,
parentsAccumulator){
var visitor = function myVisitor (node) {
var total;
function listForNode(n, parentsAcc) {
var thisPath = parentsAccumulator(parentsAcc, n);
total = totalAccumulator(total, thisPath, n);
n.childs && n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
total = totalAccumulatorInitialValue();
listForNode(node, parentsAccumulatorInitialValue());
return total;
}
return visitor;
}
var listPaths2 = createVisitor2(
function totalInit() {
return [];
},
function totalAcc(total, thisPath, n){
total.push(thisPath);
return total;
},
function parentInit () {
return "";
},
function parentAcc (parentFullPath, n) {
return parentFullPath + "/" + n.path;
});
Which might be pretty reasonable, but as you can see, I'm already beginning to have trouble finding appropriate names for these variables. In fact, I'd say the name of our function is bad, as doesn't create anything strictly like a visitor object I know of. However, it does work (BTW, I've slightly modified it to handle nulls as well as empty arrays):
> listPaths( { path:"foo",
childs: [{path:"bar", childs: null}, {path:"bob", childs: null}]})
["/foo", "/foo/bar", "/foo/bob"]
It can be modified even further so that your trees don't strictly even have the same structure... but we're already at 4 parameters, which isn't great. It'd be better if your visitor creator were passed a single extensible object with all the necessary methods or values. For instance, maybe (pseudocode):
function createVisitor3(opts) {
//assume we've defined GetDefaults() somewhere local to createVisitor3
// as well as assume that extend is defined somewhere that copies properties
// into a new object like various previously existing libraries do.
opts = extend({}, GetDefaults(), opts);
var totalAccumulatorInitialValue = opts.totalAccumulatorInitialValue;
var totalAccumulator = opts.totalAccumulator;
var parentsAccumulatorInitialValue = opts.parentsAccumulatorInitialValue;
var parentsAccumulator = opts.parentsAccumulator;
var childrenGetter = opts.childrenGetter;
/// etc.
...
}
This question already has an answer here:
Double-Queue Code needs to be reduced
(1 answer)
Closed 9 years ago.
Is there any way for me to shorten this code by using pointers?
I need to make a class that has mostly the same function as a given array class unshift,shift,push and pop but with different names.
var makeDeque = function()
{
var a= [], r=new Array(a);
length = r.length=0;
pushHead=function(v)
{
r.unshift(v);
}
popHead=function()
{
return r.shift();
}
pushTail=function(v)
{
r.push(v);
}
popTail=function()
{
return r.pop();
}
isEmpty=function()
{
return r.length===0;
}
return this;
};
(function() {
var dq = makeDeque();
dq.pushTail(4);
dq.pushHead(3);
dq.pushHead(2);
dq.pushHead("one");
dq.pushTail("five");
print("length " + dq.length + "last item: " + dq.popTail());
while (!dq.isEmpty())
print(dq.popHead());
})();
Output should be
length 5last item: five
one
2
3
4
Thanks!
Maybe I'm oversimplifying, but why not just add the extra methods you need to the Array prototype and call it directly?
I need to make a class that has mostly the same function as a given array class unshift,shift,push and pop but with different names.
I suppose you could add these "new" methods to Array.prototype.
Like this perhaps?
var makeDeque = (function (ap) {
var Deque = {
length: 0,
pushHead: ap.unshift,
popHead: ap.shift,
pushTail: ap.push,
popTail: ap.pop,
isEmpty: function () {
return !this.length;
}
};
return function () {
return Object.create(Deque);
};
})(Array.prototype);
DEMO
If it's still too long, you can always directly augment Array.prototype like others already mentionned. We agree that it's all experimental here and the only goal is to save characters.
!function (ap) {
ap.pushHead = ap.unshift;
ap.popHead = ap.shift;
ap.pushTail = ap.push;
ap.popTail = ap.pop;
ap.isEmpty = function () {
return !this.length;
};
}(Array.prototype);
function makeDeque() {
return [];
}
This can be compressed to 174 chars:
function makeDeque(){return[]}!function(e){e.pushHead=e.unshift;e.popHead=e.shift;e.pushTail=e.push;e.popTail=e.pop;e.isEmpty=function(){return!this.length}}(Array.prototype)
DEMO
Not sure why you need this, but my suggestions per best practice are:
Don't override the Array.prototype. The reason for this is because other libraries might try to do the same, and if you include these libraries into yours, there will be conflicts.
This code is not needed. var a= [], r=new Array(a);. You only need ...a = [];.
Ensure you are creating a real class. In your code, makeDeque is not doing what you want. It is returning this which when a function is not called with the new keyword will be the same as the window object (or undefined if you are using what is called as "strict mode"). In other words, you have made a lot of globals (which are usually a no-no, as they can conflict with other code too).
When you build a class, it is good to add to the prototype of your custom class. This is because the methods will only be built into memory one time and will be shared by all such objects.
So I would first refactor into something like this:
var makeDeque = (function() { // We don't need this wrapper in this case, as we don't have static properties, but I've kept it here since we do want to encapsulate variables in my example below this one (and sometimes you do need static properties).
function makeDeque () {
if (!(this instanceof makeDeque)) { // This block allows you to call makeDeque without using the "new" keyword (we will do it for the person using makeDeque)
return new makeDeque();
}
this.r = [];
this.length = 0;
}
makeDeque.prototype.setLength = function () {
return this.length = this.r.length;
};
makeDeque.prototype.pushHead=function(v) {
this.r.unshift(v);
this.setLength();
};
makeDeque.prototype.popHead=function() {
return this.r.shift();
this.setLength();
};
makeDeque.prototype.pushTail=function(v){
this.r.push(v);
this.setLength();
};
makeDeque.prototype.popTail=function() {
return this.r.pop();
this.setLength();
};
makeDeque.prototype.isEmpty=function() {
return this.r.length === 0;
};
return makeDeque;
}());
Now you could shorten this as follows, but I wouldn't recommend doing this, since, as it was well said by Donald Knuth, "premature optimization is the root of all evil". If you try to shorten your code, it may make it inflexible.
var makeDeque = (function() {
function makeDeque () {
if (!(this instanceof makeDeque)) {
return new makeDeque();
}
this.r = [];
this.length = 0;
}
makeDeque.prototype.setLength = function () {
return this.length = this.r.length;
};
for (var i=0, methodArray = [
['pushHead', 'unshift'], ['popHead', 'shift'], ['pushTail', 'push'], ['popTail', 'pop']
]; i < methodArray.length; i++) {
makeDeque.prototype[methodArray[i][0]] = (function (i) { // We need to make a function and immediately pass in 'i' here because otherwise, the 'i' inside this function will end up being set to the value of 'i' after it ends this loop as opposed to the 'i' which varies with each loop. This is a common "gotcha" of JavaScript
return function () {
var ret = this.r[methodArray[i][1]].apply(this.r, arguments);
this.setLength();
return ret;
};
}(i));
}
makeDeque.prototype.isEmpty=function() {
return this.r.length === 0;
};
return makeDeque;
}());
If you need to get the length by a length property, as opposed to a method like setLength() which sets it manually after each update, either of the above code samples could be shortened by avoiding the setLength() method, but you'd need to use the Object.defineProperty which does not work (or does not work fully) in older browsers like IE < 9.
I'm trying to follow the rule and avoid repeating the same code.
I have this single function but depending on the input I want it to either return an array of objects or an object (not an array of just one object)
e.g.(the actual function is much longer and more elaborate than this one obviously, there are just the last few lines after a much longer calculation)
function (nameParameter, ageParameter, inputType)
{
if (inputType === "asObject")
{
var x = {};
x.name = nameParameter;
x.age = ageParameter;
return x;
}
else if (inputType === "asArray")
{
var y = [];
y.push(nameParameter);
y.push(ageParameter);
return y;
}
};
Is this possible and if so is it good practice? Is there some other way around it?
Otherwise I'll have to create two distinct function with almost the exact same code.
Don't do this. Implement one version and add a wrapper function that converts the the other format you may want. That way the caller always gets consistent behaviour, and theres still no code duplication.
function asObject(nameParameter, ageParameter)
{
//Lots of work here.
var x = {};
x.name = nameParameter;
x.age = ageParameter;
return x;
};
function asArray(nameParameter, ageParameter)
{
//Just defer to the other version and repack its response.
var o = asObject(nameParameter, ageParameter);
var y = [o.nameParameter,o.ageParameter ];
return y;
}
You can simplify your code by declaring the object and array with the values already set, but in my opinion if you have this strict type of coding it is not necessary to keep this function... Anyway, here is a simplified version:
function (nameParameter, ageParameter, inputType) {
var ret;
if (inputType === "asObject") {
ret = {
name: nameParameter,
age: ageParameter
};
} else if (inputType === "asArray") {
ret = [nameParameter, ageParameter];
}
return ret;
};
I left it without name and with a semicolon at the end because I guess it has been declared through a variable.
Yes; that will work fine.
Javascript is not strongly-typed; functions can return whatever they want, whenever they want.
if ( typeof inputType == 'object') {
//object part of code
} else {
//array part of code
}
I have been trying all day to find a way to make this work. It's really bugging me. I've read loads of articles on OOP and although i know my code is not perfect, I can't seem to get around the initial problem that I have outlined in my code below.
All of the code works except the dynamic getter/setter - If you read my comments in the code or run the code you can easily see why.
I have a feeling what I'm trying to do can't be done. Can someone tell me where I'm going wrong, or if It can't be done the way i have proposed.
var ext = {
db: {
string: function(def){
def=def || {};
def.type = "string";
return def;
},
autoId: function(def){
def=def || {};
def.defaultvalue = function(){
return Math.uuid(def.length, def.radix)
return def;
}
}
},
}
/*
* #ext.base: base class I can build out with common methods
*
*/
ext.base = function(){
/*
* place holder for the raw object data -
*
* {a:"martin webb",b:"is struggling with this"
*
*/
this._rawdata={}; // this can later be strigified for storage;
};
ext.base.prototype ={
init:function(o){
// pass in rawdata;
this.load(o);
},
put:function(){
//code removed
},
load: function(rawdata){
this._rawdata=rawdata || {};
}
}
/*
* #ext.kind: builds a new object we can use to make entities from our kind
* martin=new people();
*
*/
ext.db.kind=function(o){
this._kind=function(o){this.init(o);} //this will pass any passed in object in the base class at init;
this._kind.prototype = ext.base.prototype;
var self=this;
var prop,i=97; //ascii for "a";
for (prop in o){
if (o.hasOwnProperty(prop)) {
/*
* This is like a dynamic getter setter, we are adding it to the prototype;
*
* if you run the code when the function below is called
* value="martin webb" I need the var i to equal 97, and prop to = "name"
* in the function. I need a way to pass in and conserver these dynamic values
*/
this._kind.prototype[prop] =
function(value){
if (value) {
self.rawdata[i] = value; //ERROR i=99!
}
return self.rawdata[i] ; //get the correct data using the key
};
i++;
}
}
return this._kind; // return our new object so we can use it like var martin=new people();
}
debugger;
//lets make a new kind;
var people={};
people.name=ext.db.string();
people.info=ext.db.string();
people = new ext.db.kind(people);
//and now make some entities usinhg it;
var martin=new people();
//THE CODE WILL GO WRONG HERE, THE SETTER WILL NOT WORK (SEE ABOVE)
martin.name("martin webb")
martin.info("is struggling with this code")
var mike = new people({
a: "mike",
b: "has the solution"
});
console.log(mike.name()) //--> mike
I have found the solution. The answer was to use an enclosure and trap the loop index (i).
This was beautifully explained in the article by
Explaining javascript Scope and closures
The alteration to my code is as follows and works a treat!
var prop, i = 97; //ascii for "a";
//loop each property and make our own get/setter that works
//off our underlying compressed rawdata;
for (prop in o) {
if (o.hasOwnProperty(prop)) {
/*
* this is the trick
* use an enclosure and pass our loop index in the bottom
* with a self calling function, this locks the value of i;
*/
this._kind.prototype[prop] = function(k){
return function(value){
var key=String.fromCharCode(k);
//if we have a value passed in set it!
if (value){
this._rawdata[key]=value;
}
//always return our value;
return this._rawdata[key];
};
}(i); //here we call our function and lock index i;
i++;
}//end loop;