Structs in Javascript
Asked Answered
P

11

139

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?

Partida answered 2/2, 2009 at 6:25 Comment(2)
There's a typo in the code - "spkr"Planospore
Is you concern memory efficiency? Or just type checking? For typechecking, typescript is definitely the way today. For memory efficiency, I'm still looking! #1248802Hangdog
T
202

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
Turoff answered 2/2, 2009 at 6:41 Comment(3)
I like this approach, however be careful if you use the closure compiler. The tuple can only be accessed as string in this case, because the properties are renamed. (At least in advanced mode)Sup
I was curious about the efficiency of this method over the object literals.Cordellcorder
It is maybe also possible to use JS class.Thrush
C
41

I always use object literals

{id: 1, speaker:"john", country: "au"}
Cuirassier answered 2/2, 2009 at 6:28 Comment(5)
wouldn't that make it much harder to maintain (should you want to add a new field in the future), and also much more code (retyping "id", "speaker", "country" every time)?Partida
It is exactly as maintainable as solution with classes because JavaScript doesn't care about number of arguments you call the function with. Retyping is not an issue if you using right tools like Emacs. And you can see what equals what which makes mistakes like swapping arguments obsolete.Cuirassier
But the biggest pro is that you would write less code and it'll be cleaner :)Cuirassier
@Cuirassier retyping is still an issue, as there are more jumps than copying the new ___ ( , , , ) archetype. Also, there is no readability. Once you get used to coding, new READABLE_PART(ignore everything in here) becomes very scannable and self documenting, as opposed to {read: "ignore", everything: "ignore", in: "ignore", here: "ignore"} // and then come up with READABLE_PART in your head. The first version is readable while paging up rapidly. The second, you refactor into structs merely to understand.Bombazine
Thanks for your concern, mate! I've been doing fine over the last 14 years and have a very healthy career in software engineering.Partida
L
27

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"];

Loveless answered 7/11, 2012 at 1:37 Comment(0)
W
9

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.

Waltz answered 13/8, 2011 at 22:56 Comment(2)
I'd love to have something like structs, tuples - something that allows strongly typed collections of data - that is dealt with at compiletime and doesn't have the overhead of hashmaps like objectsSubaudition
@Subaudition Agreed. Right now you can use bit arrays but it's a huge hassle so it's only for performance optimisations.Brita
D
8

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
Dodecahedron answered 14/10, 2017 at 5:48 Comment(1)
It looks like the struct in C/C++ and other languages, but actually it is not - the , properties in objects are not guaranteed to be ordered described as following: Definition of an Object from ECMAScript Third Edition (pdf): 4.3.3 Object An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive value, object, or function. A function stored in a property of an object is called a methodTripartite
A
3

I use objects JSON style for dumb structs (no member functions).

Alter answered 2/2, 2009 at 6:29 Comment(1)
But then how to re-use the structure/fields of the struct? I want to create instances of the same structure many times.Sallet
T
3

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:

  • not bound to argument order
  • easily extendable
  • type script support:

enter image description here

Thirtytwomo answered 2/5, 2020 at 1:5 Comment(0)
N
1

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 
Nannette answered 13/2, 2018 at 17:15 Comment(0)
C
0

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.

Calamanco answered 27/6, 2021 at 13:13 Comment(0)
M
0

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:

  • Flexibility
  • Reusability
  • Dynamic property assignment at runtime.

Disadvantages:

  • Lack of type checking for properties.
Malleolus answered 29/3 at 16:18 Comment(0)
C
-1

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_; 
 }
}
Cornelius answered 9/10, 2022 at 5:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.