Reputation: 546035
Previously, when I needed to store a number of related variables, I'd create a class.
function Item(id, speaker, country) {
this.id = id;
this.speaker = speaker;
this.country = country;
}
var myItems = [new Item(1, 'john', 'au'), new Item(2, 'mary', 'us')];
But I'm wondering if this is a good practice. Are there any other, better ways to simulate a struct in JavaScript?
Upvotes: 142
Views: 298783
Reputation: 1
This function allows you to create structured objects with predefined properties. When calling Struct, you pass in a constructor function that defines the properties of the struct.
function Struct(_constructor) {
const struct = [];
const o = new Proxy({}, {
get(object, prop, receiver) { struct.push({key: prop, val: undefined}); },
set(object, prop, value) { struct.push({key: prop, val: value}); },
})
_constructor(o);
return function () {
const object = {};
struct.forEach((obj,index) => {
object[obj.key] = index < arguments.length ? arguments[index] : obj.val
})
return object
}
}
// For example:
const Item = Struct(o => {
o.id
o.speaker
o.country
})
// To create items, simply do
const items = [new Item(1, 'john', 'au'), new Item(2, 'mary', 'us')];
console.log(items)
Advantages:
Disadvantages:
Upvotes: 0
Reputation: 54
This process just worked for me. Deployed Smart Contracts from Hardhat Dev-env to Ganachi Blockchain Test Net.
File: ./scripts/deploy.js
var structJS = { id: 55, title: "GP", iccid: "56", msisdn: "1712844177", imsi: "12345" };
const USIMContract = await ethers.getContractFactory("USIM");
const usimContract = await USIMContract.deploy(89, structJS);
console.log("USIM deployed to: ", usimContract.address);
Solidity Script:
struct SolStruct { uint id; string title; string iccid; string msisdn; string imsi; }
contract USIM {
uint private _iccid;
SolStruct private _communicationProfile;
constructor( uint iccid_, SolStruct memory communicationProfile_ )
{
_iccid = iccid_;
_communicationProfile = communicationProfile_;
}
}
Upvotes: -1
Reputation: 6790
The real problem is that structures in a language are supposed to be value types not reference types. The proposed answers suggest using objects (which are reference types) in place of structures. While this can serve its purpose, it sidesteps the point that a programmer would actual want the benefits of using value types (like a primitive) in lieu of reference type. Value types, for one, shouldn't cause memory leaks.
EDIT: There is a proposal in the works to cover this purpose.
//today
var obj = {fname: "Kris", lname: "Kringle"}; //vanilla object
var gifts = ["truck", "doll", "slime"]; //vanilla array
//records and tuples - not possible today
var obj = #{fname: "Buddy", lname: "Hobbs"};
var skills = #["phone calls", "basketball", "gum recycling"];
Upvotes: 28
Reputation: 3520
This is an old problem that it doesn't seem has been addressed yet. For what it's worth, I use immutability to get similar behavior. Using Typescript:
export class Point {
public readonly X: number;
public readonly Y: number;
constructor(x: number, y: number)
{
this.X = x;
this.Y = y;
}
public static SetX(value: number) : Point {
return new Point(value, this.Y);
}
public static SetY(value: number) : Point {
return new Point(this.X, value);
}
}
This gets you a key benefit of a complex value type, namely that you can't accidentally modify the object via a reference to it.
The drawback of course is that if you DO want to modify a member you have to make a new instance, hence the static SetX
and SetY
functions.
It's a lot of syntactic sugar but I think it's worth it for special cases, like Point
, that could potentially get used A LOT and lead to A LOT of bugs if values are changed accidentally.
Upvotes: 0
Reputation: 15042
It's more work to set up, but if maintainability beats one-time effort then this may be your case.
/**
* @class
*/
class Reference {
/**
* @constructs Reference
* @param {Object} p The properties.
* @param {String} p.class The class name.
* @param {String} p.field The field name.
*/
constructor(p={}) {
this.class = p.class;
this.field = p.field;
}
}
Advantages:
Upvotes: 3
Reputation: 12099
I made a small library to define struct if you work with ES6 compatibility.
It is a JKT parser you may checkout the project repository here JKT Parser
For an example you may create your struct like this
const Person = jkt`
name: String
age: Number
`
const someVar = Person({ name: "Aditya", age: "26" })
someVar.name // print "Aditya"
someVar.age // print 26 (integer)
someVar.toJSON() // produce json object with defined schema
Upvotes: 1
Reputation: 1667
Following Markus's answer, in newer versions of JS (ES6 I think) you can create a 'struct' factory more simply using Arrow Functions and Rest Parameter like so:
const Struct = (...keys) => ((...v) => keys.reduce((o, k, i) => {o[k] = v[i]; return o} , {}))
const Item = Struct('id', 'speaker', 'country')
var myItems = [
Item(1, 'john', 'au'),
Item(2, 'mary', 'us')
];
console.log(myItems);
console.log(myItems[0].id);
console.log(myItems[0].speaker);
console.log(myItems[0].country);
The result of running this is:
[ { id: 1, speaker: 'john', country: 'au' },
{ id: 2, speaker: 'mary', country: 'us' } ]
1
john
au
You can make it look similar to Python's namedtuple:
const NamedStruct = (name, ...keys) => ((...v) => keys.reduce((o, k, i) => {o[k] = v[i]; return o} , {_name: name}))
const Item = NamedStruct('Item', 'id', 'speaker', 'country')
var myItems = [
Item(1, 'john', 'au'),
Item(2, 'mary', 'us')
];
console.log(myItems);
console.log(myItems[0].id);
console.log(myItems[0].speaker);
console.log(myItems[0].country);
And the results:
[ { _name: 'Item', id: 1, speaker: 'john', country: 'au' },
{ _name: 'Item', id: 2, speaker: 'mary', country: 'us' } ]
1
john
au
Upvotes: 8
Reputation: 6137
I think creating a class to simulate C-like structs, like you've been doing, is the best way.
It's a great way to group related data and simplifies passing parameters to functions. I'd also argue that a JavaScript class is more like a C++ struct than a C++ class, considering the added effort needed to simulate real object oriented features.
I've found that trying to make JavaScript more like another language gets complicated fast, but I fully support using JavaScript classes as functionless structs.
Upvotes: 9
Reputation: 89171
The only difference between object literals and constructed objects are the properties inherited from the prototype.
var o = {
'a': 3, 'b': 4,
'doStuff': function() {
alert(this.a + this.b);
}
};
o.doStuff(); // displays: 7
You could make a struct factory.
function makeStruct(names) {
var names = names.split(' ');
var count = names.length;
function constructor() {
for (var i = 0; i < count; i++) {
this[names[i]] = arguments[i];
}
}
return constructor;
}
var Item = makeStruct("id speaker country");
var row = new Item(1, 'john', 'au');
alert(row.speaker); // displays: john
Upvotes: 202
Reputation: 69835
I use objects JSON style for dumb structs (no member functions).
Upvotes: 3
Reputation: 25371
I always use object literals
{id: 1, speaker:"john", country: "au"}
Upvotes: 41