1

I see very often to pass around unnamed data objects in JavaScript, e.g. { a: 1, b: 2}. Is it a good practice or is better to make a simple data class for that like in other languages:

class MyDataClass {
  constructor({ a, b }) {
     this.a = a;
     this.b = b;
  }
}

E.g. to improve code quality, maintainability, robustness, mistypes, etc. Some other advantages may be: parameter checking may be added, fields may be hidden by readonly properties (getters), ...

Addendum: JavaScript unnamed objects are like C# ExpandoObject. If any C# programmer would toss ExpandoObject without real purpose, then they would be fired. But unnamed objects in JavaScript are accepted.

7
  • 2
    "Better" by what metric? Readability? Runtime performance (memory footprint or cycles)? Resiliency? Flexibility? Testability? etc. etc. Some of these are the same for both implementations, and others are likely negligible difference (or can/should be solved by other avenues)
    – Delioth
    Commented Dec 11, 2019 at 19:24
  • I am asking for a good practice in general, that is, code quality, maintainability, robustness, etc. It does not mean performance. I am coming from Java, C# world, and these languages has some good practices most programmers usually follow. E.g. unnamed objects in C# are ExpandoObject - but nobody uses that except some special cases.
    – xmedeko
    Commented Dec 11, 2019 at 19:45
  • 1
    It might be a good practice to formalize the data model, but it's certainly not expected the way it would be in strongly-typed languages. I think it depends on the application, culture, and other team members.
    – Dan Wilson
    Commented Dec 11, 2019 at 20:07
  • 1
    If you seek code quality, maintainability, robustness, etc of this kind, I'd suggest you take a look at TypeScript.
    – 9000
    Commented Dec 11, 2019 at 20:08
  • 1
    This question might be too broad or opinion based, but I don't think it quite deserved this many down votes. I think this is actually answerable. Commented Dec 12, 2019 at 12:53

3 Answers 3

1

This is trending to be a very opinion based question, but here is my attempt at a non opinion based answer.


Data objects, as you are calling them, are more formally known as a Data Transfer Objects (DTO). A data transfer object is just a bag of data used to transport information between processes, be it across a network or from one literal process on a computer to another. It usually involves serializing and deserializing data.

Data transfer objects are a workaround required by strongly typed languages when you want to deal with strongly typed data. Well, "workaround" is probably the wrong word here. "Necessity" is probably a better word. The compiler needs to know ahead of time what the data types will be for all members in a data transfer object in languages like C#, Java and C++.

JavaScript and ECMA Script do not have these constraints. Not only is knowing data types ahead of time unnecessary, but a simple string representation of these data structures is natively parseable by browsers (JSON). Defining a class for a DTO is not only unnecessary, but impossible if you want to deserialize it using JSON.parse() or serialize it using JSON.stringify().

Using "data objects" or anonymously typed objects in JavaScript/ES for DTOs is not just a "good practice" but it is a requirement of the language's native data format: JSON. The same can be said about XML and HTML. The code that sits between an AJAX request and your other application code in the front end must use anonymously typed objects, but there are other use cases where anonymous types are "good practice" as well.

Using "options arguments" to a function is a common example where defining a custom type in JavaScript/ES is usually unnecessary. Most jQuery plugins are examples of this. This pattern boils down to passing configuration data. These data structures are usually very simple and have few constraints about their construction. They usually have no behavior associated with them, aside from callback functions invoked during an event. Often options arguments are used as a means to support function overloading in a language that does not support function overloading, where the function changes its behavior depending on the properties in the options argument.

The last use case for anonymous types in JavaScript/ES is for simple data structures that are not functionally considered configuration, and whose runtime data do not change the function behavior. Think of cases like foo.moveTo({ x: 3, y : -2 }) where the argument is a simple x,y point. This pattern seems to take the place of named arguments in other languages: foo.moveTo(x: 3, y: -2). This works well for simple data structures, and helps code authors call the function correctly when it takes arguments with the same type:

let x = 3;
let y = -2;

// Oops! I transposed the x and y, but the function runs without error
// and produces unexpected output:
foo.moveTo(y, x)

// Now order doesn't matter. This:
foo.moveTo({ y: y, x: x })

// Executes the same as this:
foo.moveTo({ x: x, y: y })

All of the use cases for data transfer objects above have two things in common:

  • No constraints exist at the time of object construction
  • No behavior is coupled to the data

This finally leads us to knowing when defining a custom type in JavaScript/ES is beneficial. If you need to enforce constraints at the time of object construction, then you need a constructor function:

class TimestampRange {
    constructor(begin, end) {
        if (begin > end) {
            throw new Error("Begin date cannot occur before end date");
        }

        this.begin = begin;
        this.end = end;
    }
}

If you must combine behavior with this data, then define a custom type with instance methods:

class TimestampRange {
    getElapsedTime() {
        return this.end - this.begin;
    }
}

If you do not need either of these features, then a class just bloats your code base without any benefits.

Data (transfer) objects, by their very nature, do not have constraints applied at the time of object construction and have no behavior, so anonymous types are the better choice, because JavaScript/ES is a loosely typed language.

7
  • JSON.stringify() can handle custom class, JSON.parse() produce just an Object, of course. You can write a custom mapper JSON.parse() -> Object -> custom object, though. JSON is not a reason to use plain objects.
    – xmedeko
    Commented Dec 12, 2019 at 13:03
  • I do not mean DTOs only, I mean any data object like your example TimestampRange . I see JS codes which use just a plain object for this without any checks etc. Even if you have a DTO you may add a check to constructor to be sure DTO is properly initialized, etc.
    – xmedeko
    Commented Dec 12, 2019 at 13:12
  • @xmedeko: I also addressed that in my answer when I talked about needing data constraints or behavior. If you do not need either of those, you don't need a custom type. Commented Dec 12, 2019 at 13:15
  • On the other hand, if you do need either check constraints or behavior, a custom type can be warranted. If you are using data objects where constraints or behavior are actually needed, then the code should be refactored. Commented Dec 12, 2019 at 13:16
  • I need these defensive programming checks. But most JS folks use plain objects and argue they do not need it. And that's the root of the problem in the question - why many JS code uses just unnamed JS objects without any defensive checks of their correctness. Probably most JS codes are simple and do not need it. Then JS programmers takes this habit to big projects, too and cannot see the problem.
    – xmedeko
    Commented Dec 12, 2019 at 13:29
1

If you are using JavaScript or ES6, in such a case you should use ES6 objects as they are more efficient and easier for you as well as for other developers also. Obviously, if ES6 is providing you some better functionalities then it must be more efficient. So, I suggest you use ES 6 syntax.

0

Not if you're going to name it MyDataClass. Yuck. That's a terrible name.

I'll go to a lot of trouble just so that I can give something a good name. But a bad name is worse than no name. No names come in many forms: anonymous functions, lambdas, and tuples to name a few. A known structure doesn't need a name if use is enough to make it's correct form clear.

When you give a pile of data a class name it should have meaning. The name should make clear what belongs in the class and what doesn't. The name should help make clear what forms are valid.

When your naming skills can't live up to that then please avoid naming it MyFooClass if you can. We don't need meaningless noise.

3
  • 1
    I mean, I agree with you, but I think he just chose a random name to make clear that it's supposed to be a data object, so this seems a bit pendantic and not really addressing the question
    – Joe
    Commented Dec 11, 2019 at 22:33
  • @Joe Just exploiting that to make my point. Commented Dec 11, 2019 at 22:41
  • The name was just an example. In C# (Java), you have also dynamic, ExpandoObject, anonymous functions, lambdas, and tuples etc. But it comes with a good practice and general advice when to use it.
    – xmedeko
    Commented Dec 12, 2019 at 7:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.