1

Node.js querystring.parse() method returns what looks like to be an object, but one without a constructor. According to https://nodejs.org/api/querystring.html :

"... The object returned by the querystring.parse() method does not prototypically inherit from the JavaScript Object. This means that typical Object methods such as obj.toString(), obj.hasOwnProperty(), and others are not defined and will not work."

This easily causes bugs because typically you would assume that every Object understands some basic methods like toString() and that it has a "constructor" which more or less tells us its "type".

What's the best way to handle these rather incapable Objects? I tried:

let myOb =  new Object(dumbObject);

But that produces a result which does not have the toString() -method either and does not have the property 'constructor'.

What's the best way to turn these dumb objects into ordinarily behaving ones? And, why would anybody want to create such objects in the first place?

Panu Logic
  • 2,193
  • 1
  • 17
  • 21
  • 1
    i mean... what in particular is missing that you need? you can still get an array of the keys, or convert it to json. – Kevin B Aug 21 '17 at 21:03
  • 1
    You are talking about a query string parser so that creates more of a _dto_ type object than a true _object_. As far as `toString()`, just use json serialize if that is what you need. Keep in mind also that javascript is very loosely typed so knowing the "type" doesn't mean what it would in a strongly typed language like C#. – nurdyguy Aug 21 '17 at 21:05
  • 3
    Why are you using language like "dumb objects"? Don't you understand why this is needed? What if your query string shares parameter names with prototyped properties on `Object.prototype`? – spanky Aug 21 '17 at 21:06
  • I'd like to log on console the result of any method or function or expression. So I would like to write: let s2 = require('querystring').parse("") ; console.log ( "Got value: " + s2 ) ; But that produces the error "Cannot convert object to primitive value" – Panu Logic Aug 21 '17 at 21:24
  • `This easily causes bugs because typically you would assume that every Object understands some basic methods like toString() and that it has a "constructor" which more or less tells us its "type"` Usually you would understand what you are dealing with. A plain key-value map that has not inherited properties/method whatsoever, because it's not extended from any other type. And this is not "dumb" or error prone. Not in contrast to decades where every lib could have messed up EVERY SINGLE OBJECT in your program by extending the `Object.prototype` – Thomas Aug 21 '17 at 21:24
  • 2
    @PanuLogic: Do you need to `console.log()` as a string value? Seems like logging the actual value would be more useful since consoles will generally let you expand objects to observe them. – spanky Aug 21 '17 at 21:26
  • @PanuLogic It seem like you want `console.log("Got value: "+JSON.stringify(s2))`. Or `util.inspect`, etc. – Bergi Aug 21 '17 at 21:27
  • @PanuLogic `console.log()` is smarter than an alert box. It can take multiple arguments and you can even inspect the objects you've logged. *Stop concatenating strings* in the console! Use `console.log( "Got value: ", s2 );` or if you want some [templating](https://developer.mozilla.org/en-US/docs/Web/API/console#Using_string_substitutions): `console.log( "got value: %o and I can inspect it", s2 );` – Thomas Aug 21 '17 at 21:28

5 Answers5

1

I think (from the top of my head)

let newObject = JSON.parse(JSON.stringify(dumbObject))

should work.

Rastko
  • 477
  • 2
  • 7
  • Thanks. I'll try that. I hope that works for every type of value possible, except perhaps null and undefined? Or maybe them too. As I explained in one of the comments I'm trying to log values, and would like to of course log every possible type of value with the same simple piece of code. – Panu Logic Aug 21 '17 at 21:37
  • @PanuLogic: It'll work only for primitives, excluding `undefined`, and Objects and Arrays that contain those primitives, Objects or Arrays. No functions or regex, and it will only visit own (not inherited) properties, and enumerable properties. This is meant to be used for simple, specific data serialization and sharing between different programming environments, not general data logging or debugging. – spanky Aug 21 '17 at 23:58
  • This type of conversion can cause bugs if you aren't very careful about how you use the resulting object. `querystring.parse()` probably does what it does for a very specific reason. It wants you to be able to enumerate the properties it has and have all of those be actual items present in the query string with no chance of confusing a built-in Object method with what was actually in the query string. The more modern way to do this would be with a `Map` object which handles this problem in a better way. – jfriend00 Aug 22 '17 at 00:38
1

If you want a more generic way to call toString() on an object, you can call it from Object.prototype using .call().

var s = Object.prototype.toString.call(smartObject);

But why? It's just going to give you "[object Object]" on any plain object. Why is it important to get that string?

var p = {};
var s = Object.create(null);

console.log(p.toString());
console.log(Object.prototype.toString.call(s));
spanky
  • 2,768
  • 8
  • 9
  • Why is it important to get that string? Because (one example) I want to log data on the console or file. Then things like let s2 = require('querystring').parse("") ; console.log ( "Got value: " + s2 ) ; crash. It's fine if I see [object Object] but not so fine if it crashes. If I need to, and now it seems I do need to, assume that the logged things can crash when I try to convert them to String it means my logging code needs to get more complicated than I would like it to be. – Panu Logic Aug 22 '17 at 23:21
  • @PanuLogic: Not wanting to throw an error makes sense, but that's an answer to a different question. Yes, JS is loose. All the type checking is on your shoulders at runtime. You don't need to worry about `.toString()` if you let the logger handle it. You can do `console.log("Logging: %s", myObject);` as long as you don't actually care about the data. – spanky Aug 23 '17 at 00:37
1

typically you would assume

No, you wouldn't. If you make such assumptions, document them in your interface.

What's the best way to turn these dumb objects into ordinarily behaving ones?

Use Object.assign with an ordinary object as the target. Alternatively, you can also change the prototype by using Object.setPrototypeOf, but that's not recommended.

Or just create the properties like .toString or .constructor that you need. See this example.

And, why would anybody want to create such objects in the first place?

Because you need this safety when using objects instead of Maps. See this example.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks for the "this example" -link. I see that the other questioner had pretty much the same question as I did and answers on that page are useful as well – Panu Logic Aug 22 '17 at 23:31
0

Explicitly set the prototype of your dumb object with Object#setPrototypeOf

var dumbObject = Object.create(null);
Object.setPrototypeOf(dumbObject, Object.prototype);
dumbObject.toString() //runs fine
JellyRaptor
  • 725
  • 1
  • 8
  • 20
0

Thanks for all the answers, one of which contained a link to another question which really was my question as well, even if I didn't know that at first. That other question is: How to create a JS object with the default prototype from an object without a prototype?

From it I found the answer which I think is the simplest solution so far: Use Object.assign(). Like this:

let orphan = require('querystring').parse("{}") ;
// console.log ( "Got value: " + orphan ) ;
// Above causes the error:
// -> TypeError: Cannot convert object to primitive value
let oa     =  (x) => Object.assign({}, x);
console.log ("Got value: " + oa (orphan) ) ;

Note the issue is not particularly about "querystring" but with objects which have no prototype in general. Yes we should probably call these poor objects "orphans" instead of "dumb". But I think "dumb" is still quite good term as well. An object which has no prototype has very few (or no?) methods so it can answer very few if any questions we would like to ask it.

Panu Logic
  • 2,193
  • 1
  • 17
  • 21