I am trying to understand the OOP with the following example below. Can you please explain what am I doing wrong and why?
var shoppingcartModel = function() {
var _Cart = function() {
return {
totalPrice: {},
products: []
};
}
return {
cart: _Cart,
addProducts: function(product) {
return _Cart().products.push(product);
}
};
};
var shoppingCart = shoppingcartModel()
console.log(shoppingCart.cart())
shoppingCart.addProducts('product1')
shoppingCart.addProducts('product2')
console.log(shoppingCart.cart())
_Cart is a function that returns an object, not an object itself. Whenever you call Cart_(), including in addProducts, you create a new object, so whatever you push to one of the old objects is disregarded because no reference to the old object remains.
Try something like this instead:
var shoppingcartModel = function() {
const cart = {
totalPrice: {},
products: []
};
return {
cart,
addProducts: function(product) {
return cart.products.push(product);
}
};
};
var shoppingCart = shoppingcartModel()
console.log(shoppingCart.cart)
shoppingCart.addProducts('product1')
shoppingCart.addProducts('product2')
console.log(shoppingCart.cart)
Related
I expect that I can get the list by the a.list, but this code does not work except using original value return.
function myclass() {
this._v = {
list: []
};
}
Object.defineProperty(myclass.prototype, 'list', {
get: function() {
// return this._v.list;
return this._v.list.map(val => {
console.log('val', val);
return val;
});
}
});
var a = new myclass();
a.list.push('abc');
console.log(a.list);
The getter is always returning a new empty array when called. When you push to it, there are no observable effects, because the array that's being changed isn't the array on the object.
You need to get a reference to the _v.list array so that you can push to it, which you can do either by reference it directly
function myclass() {
this._v = {
list: []
};
}
Object.defineProperty(myclass.prototype, 'list', {
get: function() {
return this._v.list.map(val => {
return val;
});
}
});
var a = new myclass();
a._v.list.push('abc');
console.log(a.list);
Or add a different method that returns the _v.list array:
function myclass() {
this._v = {
list: []
}
}
myclass.prototype.getArr = function() {
return this._v.list;
}
Object.defineProperty(myclass.prototype, 'list', {
get: function() {
return this._v.list.map(val => {
return val;
});
}
});
var a = new myclass();
a.getArr().push('abc');
console.log(a.list);
Maybe you wanted to push the elements in the _v.list and retrieve them from new list on myclass.
function myclass() {
this._v = {
list: []
};
}
Object.defineProperty(myclass.prototype, 'list', {
get: function() {
// return this._v.list;
return this._v.list.map(val => {
return val;
})
}
});
var a = new myclass();
a._v.list.push('abc');
console.log(a.list)
In your code you have overridden get method for list which is taking all its elements from _v.list but while pushing elements to list you are not pushing them to _v.list. That's why you are getting empty array which is _v.list not list.
'use strict';
function myclass() {
this._v = {
list: []
};
}
Object.defineProperty(myclass.prototype, 'list', {
get: function() {
// return this._v.list;
console.log('list', this._v.list);
return this._v.list.map(val => {
return val;
});
},
set: function(newVal) {
this._v.list.push(newVal);
}
});
const a = new myclass();
console.log(a);
a.list = 'abc';
console.log(a.list);
I am trying to display(log) the items that I added using the addItems() function when I call(log) the getItems() function..
console.log(cart.addItem("ITEMMSSS", 100, 10)) << puts out
ShoppingCart { itemName: 'ITEMMSSS', quantity: 100, price: 10 }
as expected
but the console.log(cart.getItems()) puts out -1-
when I console.log(this.addedItems) it logs out -undefined-(twice)
I don't understand why I don't have access to the returned value from the
addItem() function.
class ShoppingCart {
constructor(itemName, quantity, price) {
this.itemName = itemName
this.quantity = quantity
this.price = price
}
addItem(...items) {
const addedItems = new ShoppingCart(...items)
return addedItems
}
getItems(addedItems) {
const el = []
const selected = this.addedItems
const newArr = el.push(selected)
return newArr
}
clear(...item) {
// return items.slice(0, ...items).concat(items.slice(...items + 1))
}
clone(...items) {
// console.log(this)
// copiedCart.map((item) => {
// return item
// })
}
}
FIXed the issue,
class ShoppingCart {
constructor(items) {
this.items = []
}
addItem(name, quantity, pricePerUnit) {
const shopCart = this.items.push({
name: name,
quantity: quantity,
pricePerUnit: pricePerUnit
})
return shopCart
}
getItems(...items) {
const displayItems = this.items
return displayItems
}
clear(...items) {
const emptyCart = this.items.length = []
return emptyCart
}
clone(...items) {
const copyCart = new ShoppingCart()
copyCart.items = JSON.parse(JSON.stringify(this.items))
return copyCart
}
}
//
// const cart1 = new ShoppingCart('banana', 12, 23)
// const cart2 = cart1.clone()
// //
// console.log(cart2)
// //
module.exports = ShoppingCart;
But can't seem to get an immutable copy of the shoppingCart <--
fixed issue after reading about deep copying
clone(...items) {
const copyCart = new ShoppingCart()
copyCart.items = JSON.parse(JSON.stringify(this.items))
return copyCart
}
Couple ideas for author to think about:
1) addItem should be a function of an existing instance of ShopppingCart. Creating a new object inside addItems should not be necessary. I only know of returning a value from a setter to be usually only done for "fluent" setters practices so that you can chain them together. But that would be returning the current object.
2) getItems should usually not perform any logic. Usually getters return the current state of a variable / object member.
To address authors direct question:
You are returning the addItems object from the function but not storing it.
Try:
cart = cart.addItem("ITEMMSSS", 100, 10)
I have been stuck with this issues for 2 hours now and I really can't seem to get it work.
const app = new Vue({
el: '#book-search',
data: {
searchInput: 'a',
books: {},
},
methods: {
foo: function () {
axios.get('https://www.googleapis.com/books/v1/volumes', {
params: {
q: this.searchInput
}
})
.then(function (response) {
var items = response.data.items
for (i = 0; i < items.length; i++) {
var item = items[i].volumeInfo;
Vue.set(this.books[i], 'title', item.title);
}
})
.catch(function (error) {
console.log(error);
});
}
}
});
When I initiate search and the API call I want the values to be passed to data so the final structure looks similar to the one below.
data: {
searchInput: '',
books: {
"0": {
title: "Book 1"
},
"1": {
title: "Book 2"
}
},
Currently I get Cannot read property '0' of undefined.
Problem lies here:
Vue.set(this.books[i], 'title', item.title);
You are inside the callback context and the value of this is not the Vue object as you might expect it to be. One way to solve this is to save the value of this beforehand and use it in the callback function.
Also instead of using Vue.set(), try updating the books object directly.
const app = new Vue({
el: '#book-search',
data: {
searchInput: 'a',
books: {},
},
methods: {
foo: function () {
var self = this;
//--^^^^^^^^^^^^ Save this
axios.get('https://www.googleapis.com/books/v1/volumes', {
params: {
q: self.searchInput
//-^^^^--- use self instead of this
}
})
.then(function (response) {
var items = response.data.items
var books = {};
for (i = 0; i < items.length; i++) {
var item = items[i].volumeInfo;
books[i] = { 'title' : item.title };
}
self.books = books;
})
.catch(function (error) {
console.log(error);
});
}
}
});
Or if you want to use Vue.set() then use this:
Vue.set(self.books, i, {
'title': item.title
});
Hope this helps.
yep, the problem is about context. "this" returns not what you expect it to return.
you can use
let self = this;
or you can use bind
function(){this.method}.bind(this);
the second method is better.
Also google something like "how to define context in js", "bind call apply js" - it will help you to understand what is going wrong.
// update component's data with some object's fields
// bad idea, use at your own risk
Object
.keys(patch)
.forEach(key => this.$data[key] = patch[key])
While experimenting with nodejs I encountered a problem of enabling isntances creation via Constructors. I create simple cart basket functionality.
I got file cart.js
var items = [];
function addItem (name, price) {
items.push({
name: name,
price: price
});
}
exports.total = function () {
return items.reduce(function (a,b) {
return a + b.price;
}, 0);
};
exports.addItem = addItem;
I run it with node
var cart = require('./cart')
But what if I need to create multiple instances of a Cart?
I tried to refactor my code, creating a Constructor, that holds items[] addItem() and total() functions, like this:
exports.Cart = function () {
var items = [];
function addItem (name, price) {
items.push({
name: name,
price: price
});
}
function total () {
return items.reduce(function (a,b) {
return a + b.price;
}, 0);
}
};
I run it like this:
var cart = require('./cart');
cart.addItem('Pepsi',199); // no problem with this
cart2 = new cart.Cart(); // it gives me undefined can't be a function
I understand, that I can use PROTOTYPE property to add functions and props to my Cart
So I create a second file cart2.js and place something like:
function Cart () {
this.items = [];
}
Cart.prototype.addItem = function (name, price) {
this.items.push({
name: name,
price: price
});
};
Cart.prototype.total = function () {
return this.items.reduce(function (a,b) {
return a + b.price;
}, 0);
};
module.exports = Cart;
And now it works.
But in order to explore all possiblities, I want to know how I can solve it the first way I tried. When I can use it as "instanceble" Class thing and as singleton thing, with only one instance, at the same time.
Can you please advice me how to solve it the way I wanted in the first place?
I'll appreciate if you provide some other ways to solve this task of creating instanceable Classes.
The first option might look like this:
exports.Cart = function () {
var items = [];
// ...other private stuff...
return {
addItem: function (name, price) {
items.push({
name: name,
price: price
});
},
total: function() {
return items.reduce(function (a,b) {
return a + b.price;
}, 0);
}
// ...other public stuff...
}
};
Usage:
var carts = require('carts');
firstCart = carts.Cart();
second = carts.Cart();
I'd like to create a Javascript object that can save and load its state (to local storage).
This is the basic pattern I'm using:
var obj = function () {
// private members
//
return {
// public members
load: function () {
this.state = JSON.parse(localStorage.getItem('obj'));
if (this.state === null) {
this.state = {
name: 'foo'
};
}
},
save: function () {
localStorage.setItem('obj', JSON.stringify(this.state));
}
};
}();
// load state
obj.load();
console.log(obj.state.name);
// save state
obj.state.name = 'bar';
obj.save();
But there's one thing that annoys me about this pattern: I have to access the object's persistent properties through the 'state' property.
How can I rewrite this so I can use the object in a more natural way, like:
// load state
obj.load();
console.log(obj.name);
// save state
obj.name = 'bar';
obj.save();
This is a very simple 'state', but the solution has to work for a complex state object with nested objects, arrays etc., so simply adding a 'name' property to my object is not what I'm after.
If you don't care which properties get loaded/saved then you can simply copy all from state into self. For example, after reading into var state (instead of this.state since you don't want state to be a part of this anymore): for(x in state) this[x] = state[x];
similarly, you'd save out: var state = {}; for(x in this) state[x] = this[x]
However, if you want to have a pre-defined list, then I'd recommend: var fields = ['name', 'zip', 'age'];
And then use for(x in fields) this[x] = state[x] to load and for(x in fields) state[x] = this[x]; to save.
Sorry it's a bit pieced together, but I hope you can follow what I mean :)
EDIT: Added full example per OPs request.
An example of a full solution using this technique is as follows:
var obj = function () {
// private members
//
return {
// public members
load: function () {
var state = JSON.parse(localStorage.getItem('obj'));
if(state == null) state = { name: 'foo' };
for(x in state) this[x] = state[x];
},
save: function ()
{
var state = {};
// check if it's a function. This version taken from underscorejs
var isFunction = function(obj) {
return !!(obj && obj.constructor && obj.call && obj.apply);
};
for(x in this)
{
if(isFunction(this[x])) continue; // skip functions
state[x] = this[x];
}
localStorage.setItem('obj', JSON.stringify(state));
}
};
};
You can also accomplish a direct save when a property changes,
by using ES5 getters/setters or by using Watch.js
Watch.js example:
var obj = (function () {
// private members
//
var self = {
// Some properties
name: '',
otherName: '',
// Try to load state or use "foo state"
state: JSON.parse(localStorage.getItem('obj')) || {
name: 'foo'
},
save: function () {
localStorage.setItem('obj', JSON.stringify(this.state));
}
};
// Watch the object and save it to local storage, when a property changes
// (Of course, you don't need to call the save method here...)
watch(self, function(property, value) {
console.log('saving state!');
self.state[property] = value;
self.save();
});
return self;
}());
// Update some properties and see that it is saved to local storage.
obj.name = "Some name";
obj.otherName = "Some other name";
console.log(JSON.parse(localStorage.getItem('obj')));
Example on JsFiddle.
You could make the state internal and surface getters and setters:
var obj = function () {
// private members
var state = {};
return {
// public members
load: function () {
var loadedState = JSON.parse(localStorage.getItem('obj'));
if (loadedState === null) {
state = {
name: 'foo'
};
} else {
state = loadedState;
}
},
save: function () {
localStorage.setItem('obj', JSON.stringify(state));
},
getState: function (key) {
return state[key];
},
setState: function (key, value) {
state[key] = value;
}
};
};
Using jQuery's extend():
var obj = (function () {
return {
load: function () {
var stored = localStorage.getItem("obj");
var state = stored ? JSON.parse(stored) : {
name: 'foo'
};
$.extend(this, state);
},
save: function () {
localStorage.setItem("obj", JSON.stringify(this));
}
};
})();
// load state
obj.load();
console.log(obj);
// save state
obj.name = 'bar';
obj.save();
jsfiddle
All credit to pimvdb.