4

Background: The module query-string is for example able to parse key=value&hello=universe to an object {key: 'value', hello: 'universe'}. However, the module author has decided that the returned object does not have a prototype. In other words, this "bastard" object is created by Object.create(null).

Problem: It would be convenient to use parsed.hasOwnProperty('hello') but that is not possible without the default object prototype. Of course, one could Object.prototype.hasOwnProperty.call(parsed, 'hello') but I think we can all agree that such an expression is kill-immediately-after-birth ugly.

Question: How to nicely convert the prototypeless object to have a default object prototype and methods such as hasOwnProperty? Additionally, can this be done without using the feared __proto__ or setPrototypeOf?

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Akseli Palén
  • 27,244
  • 10
  • 65
  • 75

4 Answers4

4

It would be convenient to use parsed.hasOwnProperty('hello') but that is not possible without the default object prototype

The whole point of creating such a "bastard object" is that you cannot do that - what if someone sent a query string ?hasOwnProperty=oops to your server?

How to nicely convert the prototypeless object to have a default object prototype and methods such as hasOwnProperty?

Don't. You should either use the long form with call, or just go for the in operator which does exactly what you need:

'hello' in parsed

In an ES6 environment, you might also want to convert the object to a proper Map and use it has method.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
2

I wouldn't recommend changing the prototype of a third party package, it may be frozen and prone to runtime errors. I'd either use the built-in in operator as @Bergi suggested or the ES6 Reflect API.

const _ = console.info

const parsed = (
  { __proto__: null
  , foo: 'fu'
  , bar: 'bra'
  }
)

_('foo' in parsed) // true

_('fu' in parsed) // false


/** ES6 (fully supported in current browsers) */

_(Reflect.has(parsed, 'foo')) // true

_(Reflect.has(parsed, 'fu')) // false
cchamberlain
  • 17,444
  • 7
  • 59
  • 72
0

I can't say I've ever done this before, but here's one way to do it

let bastard = Object.create(null);
bastard.father = 'vader';

let adopted = Object.assign({}, bastard);
console.log(adopted.hasOwnProperty('father')); // => true
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • Dude, you used the Force! Excellent, exactly what I was looking for. Thanks! – Akseli Palén Oct 03 '16 at 20:57
  • 1
    @AkseliPalén you should read Bergi's post and consider accepting it as the correct answer. – Mulan Oct 03 '16 at 21:39
  • Your answer was exactly what my question was looking for. The query string provided only an example and I did not meant it to limit the context. Unfortunately, in that example context, your answer has downsides. I am sorry you received downvotes due to this. Nevertheless, I changed Bergi's answer to be the new correct one, as it provides valuable insight. – Akseli Palén Oct 04 '16 at 21:13
  • @AkseliPalén no problem. Bergi is highly knowledgeable; I've always had good success following his/her advice. – Mulan Oct 04 '16 at 21:31
0

setPrototypeOf() is nothing to be feared of, yet without it you might do as follows;

var o1 = Object.create(null),
    o2;
o1.test = "42";
o2 = Object.assign({},o1);
console.log(o2.test);
console.log(o2.constructor.prototype);
Redu
  • 25,060
  • 6
  • 56
  • 76