I was wondering if there is a way to define attributes of a class that are not defined until the instance of an object is created. Lets say I want to have a object that works with multiple attributes, in example: name, width, weight, color. But some other times I'd like to use the same object to instantiate different properties: name, length, type, material.
class MultiProduct{
constructor(atttobedefined1=a1, atttobedefined2=a2, atttobedefined3=a3, atttobedefined4=a4){
this.atttobedefined1 = a1;
this.atttobedefined2 = a2;
this.atttobedefined3 = a3;
this.atttobedefined4 = a4;
}
}
var MP = new MultiProduct(name="tesla x",width="280cm",Weight="2000T",Color="Red");
At the end, When It comes to show the object on screen, depending on the properties, it would/could be used like:
console.log(MP['width']); //in case this property 'width' exists
//output "280cm"
Is there any way to get this way or every time I have a different property/real product to be represented I have to create a new object with its setters and getters? I ask this for JavaScript in particular, but I wonder if programmatically (in general) is possible.
Excuse the bad English and the non-sense code written to be used as example.
If you want to pass key/value pairs in an argument, then use an object.
new MultiProduct(
{ property: "name", value: "tesla x" },
{ property: "width", value: "280cm" },
etc
)
Or
new MultiProduct(
{
name: "tesla x",
width: "280cm",
etc
}
)
If you want to assign to a property where the name is in a variable, use square bracket notation:
const atttobedefined1 = /* How you read from the args depends on the specific forfmat */
this[atttobedefined1] = /* etc */
Having completely freeform properties will rather lose the benefits of using classes in the first place, so you might want to rethink the approach. You might be better off with just more and optional arguments.
Related
I am unsure of why I would exactly need to use a class here or perhaps a better way to say it is: I am not sure how a class is helpful as opposed to just forming objects on the fly.
export default class Car {
constructor(type="not specified", engine="V6") {
this.type = type;
this.engine = engine;
}
getType() {
return `the car type is ${this.type}`
}
}
main.js
import Car from Car.js;
let allCars = [];
function userSubmittedCarInfo() {
let typeValue = document.getQuerySelector('.input-type').value;
let engineValue = document.getQuerySelector('.input-engine').value;
// not sure the difference of just sending data as an object vs sending as class object?
//option 1 .... for object on the fly that I can post to server. Push it to list if I
// need a running list of all objects later on.
let obj = {
type: typeValue,
engineValue: engineValue,
}
allCars.push(obj);
//option 2... second option is create an instance of Car class
let obj = new Car(typeValue, engineValue)
fetch('url-i-am-posting-to', {
car: obj
})
}
Classes are generally useful when you want to tie together data with methods that operate on that data. Your Car here gives instances both properties on the instance (data) as well as a method that operates on the data (getType).
If you actually do want to call the getType method at certain points in the code, or if you add additional methods on the Car, having a class is quite a reasonable choice - you just have to pass in the data, and it'll return an object containing both the data and useful methods for that data.
But if you don't need methods - like in this example, it doesn't look like you're ever calling getType - then a class may well not provide any benefit, and could be considered to only be adding confusing overhead, and using an object literal would make good sense instead (arguably, even more sense).
I want to make an object that inherits from an other object. In constructing the descendant, i want to push some items to an inherited array without changing the parentobject.
Say I have an object called basket:
function Basket(){
}
Then i fill it like this:
Basket.prototype {
“price”: 5,
“contents” : [“apple”, “orange”, “grape”]
}
Now i want to extend this. I want to add some properties and change some. I did this:
function BigBasket(){
this.price = 6; // change a property. This goes well, when an instance is created, price is still 5 in prototype and also in instances of Basket and it is 6 in the instance that is created from this descendant.
this.greetingcard = “Congratulations” // add a property. Goes well
Now i want to add an item to the contents-property but only in the descendant instances.
This goes wrong:
this.contents.push(“banana”);
It seems this.contents contains a reference to the array of the prototype so when pushing a banana to it, means that instances of both Bigbasket and Basket get a banana in their contents as well. Therefore i first made a copy of the Basket.contents (the parent contents) like this:
this.contents = Object.getPrototypeOf(this).contents.slice(); // seems __proto__ is deprecated, so using getPrototypeOf here and then pushed the banana:
this.contents.push(‘banana’);
}
This seems to be working, but is this the right way? I know array is an object as well, so i tried this:
this.contents = Object.create(Basket.prototype.contents);
this.contents.push('banana');
This works too and seems a more generic way. Furthermore in Chrome the item that was pushed last was a property of only the descendant while the rest of the array-items were properties of the prototype. Seems elegant to me.
Still, this looks a bit clumsy to me. Am i doing this all wrong? Tried to find out but couldn’t find anything on the topic of array’s in extending objects. Is there a way of making descendants where all properties are copied and not referenced when instanciated?
Thanks!
You can try something like this:
Instead of putting price and content on prototype, make them as properties of Basket.
Inherit BigBasket and set its prototype as instance of Basket. This will give you access to properties of Basket.
Define a private variable content in BigBasket whose job will be to maintain the child's content.
Add a setter function to mutate this variable
addContent: Add new values to child's content
removeContentByIndex: to remove value based on index.
getContent: This will return a copy of content to nullify side effect. This will also allow you to have custom definition of content. In this case, its own content + parent's content
this will allow you to define an API to communicate with content and also allows you to create a restrict data that is exposed. Exposing everything can cause issues. Exposing what is required is always better.
Note: Having objects on prototype can cause issues as objects will have side effect. You should use Parent's values as default values only.
function Basket() {
this.price = 5;
this.contents = ["apple", "orange", "grape"]
}
function BigBasket() {
const content = [];
this.price = 6;
this.greetingcard = "Congratulations";
this.addContent = function(value) {
content.push(value);
}
this.removeContentByIndex = function(index) {
content.splice(index, 1);
}
this.getContents = function() {
return [].concat(this.__proto__.contents, content);
}
}
BigBasket.prototype = new Basket();
const bb1 = new BigBasket();
const bb2 = new BigBasket();
bb1.addContent('banana');
console.log(bb1.getContents(), bb2.getContents())
Consider classes
Product = {
variations: [],
properties: []
}
Property = {
values: []
}
Variation = {
values: []
}
Value = {
simpleString:""
}
On the client (in JS) I first create products with properties and the possible values each property may have.
After that I create variations. Each of them can have a set of several values. But it only can "use" the values, which do already exist for properties.
In the next step, I move that product-object over to rest api.
While in JS the values used in a variation points to the original object in the product property, after encoding to JSON and backwarts to stdClass in PHP behind the restful service, this link gets lost.
Of course I could give them IDs on my own and call them temp_id or so. Hacky.
How would pros do it?
PHP
I simply deserialize the incomming JSON $object = json_decode($json); and use RedBeans and Piped to manage that end. What it does is mainly just moving through the properties and create objects with ..._id == null (then they get a unique ID).
Let's say I would do it manually:
I'd walk through the properties and store each value-object into the MySQL-db. Say
{
value_id: null,
simpleString: 'Green'
}
After storing it will have the value_id = 111.
Now I walk through the variations. One of these uses the Green-Value. But since this value will also be
{
value_id: null,
simpleString: 'Green'
}
how could I identify them? The string, of course, is too weak - no one could consider that. As mentioned above, I could give them temporary IDs. But surely there is a better way.
This is a fairly common question here in SO, and I've looked into quite a few of them before deciding to ask this question.
I have a function, hereby called CheckObjectConsistency which receives a single parameter, an object of the following syntax:
objEntry:
{
objCheck: anotherObject,
properties: [
{
//PropertyValue: (integer,string,double,whatever), //this won't work.
PropertyName: string,
ifDefined: function,
ifUndefined: function
}
,...
]
}
What this function does is... considering the given parameter is correctly designed, it gets the objCheck contained within it (var chk = objEntry.objCheck;), It then procedes to check if it contains the properties contained in this collection.
Like this
for(x=0;x<=properties.length;x++){
if(objCheck.hasOwnProperty(properties[x].PropertyName)){
properties[x].ifDefined();
}
else{
properties[x].ifUndefined();
}
What I want is... I want to bring it to yet another level of dynamicity: Given the propositions that IfDefined and IfUndefined are functions to be called, respectively, if the currently-pointed PropertyName exists, and otherwise, I want to call these functions while providing them, as parameters, the very objCheck.PropertyName's value, so that it can be treated before returning to the user.
I'll give a usage example:
I will feed this function an object I received from an external provider (say, a foreign JSON-returning-WebService) from which I know a few properties that may or may not be defined.
For example, this object can be either:
var userData1 = {
userID : 1
userName: "JoffreyBaratheon",
cargo: "King",
age: 12,
motherID : 2,
//fatherID: 5,--Not defined
Status: Alive
}
or
var userData2 = {
userID :
userName: "Gendry",
cargo: "Forger Apprentice",
//age: 35, -- Not Defined
//motherID: 4,-- Not Defined
fatherID: 3,
Status: Alive
}
My function will receive:
var objEntry=
{
objCheck: userData1,
properties: [
{
PropertyName: "age",
ifDefined: function(val){alert("He/she has an age defined, it's "+val+" !");},
ifUndefined: function(){alert("He/she does not have an age defined, so we're assuming 20.");},
},
{
PropertyName: "fatherID",
ifDefined: function(val){alert("He/she has a known father, his ID is "+val+" !");},
ifUndefined: function(){alert("Oh, phooey, we don't (blink!blink!) know who his father is!");},
}
]
}
CheckObjectConsistency(objEntry); // Will alert twice, saying that Joffrey's age is 12, and that his father is supposedly unknown.
ifDefined will only actually work if, instead of properties[x].ifDefined();, I somehow provide it with properties[x].ifDefined(PropertyValue);. And here, at last, lies my question.
Being inside the consistency-checking-function, I only know a given property's name if it's provided. Being inside it, I can't simply call it's value, since there is no such function as properties[x].ifUndefined(properties[x].GetValueFromProperty(properties[x].PropertyName)) ,... is there?
I'm sorry. Not being a native english speaker (I'm brazilian), I can't properly express my doubts in a short way, so I prefer to take my time writing a long text, in an (hopefully not wasted) attempt to make it clearer.
If, even so, my doubt is unclear, please let me know.
I think you're looking for the bracket notation here. It allows you to provide an arbitrary value as key to access the object. Also, you know its name. You have your properties object right?
objEntry.properties.forEach(function(property){
// Check if objCheck has a property with name given by PropertyName
if(!objEntry.objCheck.hasOwnProperty(property.PropertyName)){
// If it doesn't, call isUndefined
property.isUndefined();
} else {
// If it does, call isDefined and passing it the value
// Note the bracket notation, allowing us to provide an arbitrary key
// provided by a variable value to access objCheck which in this case is
// the value of PropertyName
property.isDefined(objEntry.objCheck[property.PropertyName]);
}
});
Oh yeah, forEach is a method of arrays which allows you to loop over them. You can still do the same with regular loops though.
I have a JavaScript object that is treated as an associative array. Let's call it "fields". It has several elements, e.g.:
fields['element1'] = ...
fields['element2'] = ...
fields['element3'] = ...
Given fields[0], is it possible to obtain the name of the property (which is "element1") instead of its value?
Let's say you have an object oObject. It could be:
var oObject = {} ;
oObject["aaa"] = "AAA" ;
oObject["bbb"] = "BBB" ;
oObject["ccc"] = "CCC" ;
oObject["ddd"] = "DDD" ;
oObject["eee"] = "EEE" ;
Now, let's say you want to know its properties' names and values, to put into the variable strName and strValue. For that you use the "for(x in o)" construct, as in the following example:
var strName, strValue ;
for(strName in oObject)
{
strValue = oObject[strName] ;
alert("name : " + strName + " : value : " + strValue) ;
}
The "for(x in o)" construct will iterate over all properties of an object "o", and at each iteration, will put in variable "x" the current property name. All you have to do, then, to have its value, is to write o[x], but you already knew that.
Additional info
After some thinking, and after seeing the comment of Hank Gay, I feel additional info could be interesting.
Let's be naive (and forget the "in JavaScript, all objects, including arrays, are associative containers" thing).
You will usually need two kind of containers: Maps and Arrays.
Maps are created as in my example above (using the "o = new Object() ;" or the "o = {} ;" notation, and must be accessed through their properties. Of course, maps being maps, no ordering is guaranteed.
Arrays are created differently, and even if they can be accessed as maps, they should be accessed only through their indices, to be sure order is maintained.
Point is:
If you need a map, use a "new Object()" container
If you need an array, une an array, use a "new Array()" container
Don't EVER mix the two, and don't EVER access the map through indices, and for arrays, ALWAYS access its data through its indices, because if you don't follow those principles, you won't get what you want.
No, for two reasons.
fields[0] and fields["element1"] are different properties.
properties in an object are explicitly unordered
You could loop over the properties:
function (obj) {
for (prop in obj) {
if (obj.hasOwnProperty(prop) {
return prop;
}
}
};
…to get the "first" property for some arbitrary value of "first" that could change at any time.
http://ajaxian.com/archives/fun-with-browsers-for-in-loop explains the hasOwnProperty pattern.
There is no fields[0] (unless fields is an Array object, which supports numerical indices), so you can't get its name just like that. But you can simulate it like this:
function getKey(obj, i) {
var j = 0;
for (var p in obj) {
if (j++ == i) return p;
}
return null;
}
for (var p in obj) will loop through every field name in the object obj. By getting the nth field name, you can effectively get the "key" for a certain index.
Note that while it's working its way to become a standard, the order of field names is currently not guaranteed according to the standards, which means that after modifying the object, the same function call could theoretically return a different field name. Same thing goes that different browsers can return different results. Practically, you'll find that just about all the browsers do keep the order of field names so you shouldn't have to worry about it at all.
Just to point out what is implicit in everyone else's answer: "associative arrays" in Javascript are actually just Object instances, e.g.,
var aa = {};
aa.foo = 'argle';
alert(aa['foo']); // Will alert 'argle'
PLEASE don't use an Array instead of an Object—it has the potential to wreak havoc on for key in aa-style iteration.