2

Probably a naive question - if this snippet is executed (I'm running with Node.js v12.16.3, same result in Chrome apart from the error type):

const obj = {}

Object.defineProperty(obj, 'a', {
  writable: false,
  value: 13
})

obj.a = 14

It will, obviously, throw an error, but the message string of that error has strange representation of an object:

TypeError: Cannot assign to read only property 'a' of object '#<Object>'

The question is - why it's stringified as #<Object>?

Defining a toString method on the object will render it as if it were called with Object.prototype.toString.call(obj):

const obj = {
  toString () {
    return 'stringified obj'
  }
}

Object.defineProperty(obj, 'a', {
  writable: false,
  value: 13
})

console.log(`${obj}`)

obj.a = 14

TypeError: Cannot assign to read only property 'a' of object '[object Object]'

While console.log outputs correctly. How to make that TypeError output the correct string representation? What does it use internally?

2
  • interestingly this happens only with "use strict";. Youre using babel in your snippet. Babel uses strict-mode. Without strict mode no error will be shown. Maybe it has something to do with the strict mode and the error - handling of it. Commented Mar 13, 2021 at 21:29
  • Correct, this error is explicitly thrown only in strict mode, but I have no evidence that the strict mode represents objects differently. Probably it's just an internal switch to throw or silently do nothing. Commented Mar 15, 2021 at 13:19

1 Answer 1

1
+50

Node.js and Chrome share the same JavaScript engine called V8. That engine hard-codes the string "#" in the error message.

It uses the template:

T(StrictReadOnlyProperty,                                                    \
    "Cannot assign to read only property '%' of % '%'") 

Which in turn gets interpolated as the object's constructor name. In this case Object. You can see this:

const obj = {};
obj.constructor.name; // Object

const obj2 = new class Bar {};
obj2.constructor.name; // Bar

Or, in your example:

const obj = new class SomeValue {}

Object.defineProperty(obj, 'a', {
  writable: false,
  value: 13
})

// TypeError: Cannot assign to read only property 'a' of object '#<SomeValue>'
obj.a = 14

Note the spec just says: "throw a TypeError exception." - this behaviour is entirely defined by the V8 engine and not JavaScript.

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you for the explanation. It is interesting though that defining toString on the object changes the object representation in the error message to [object Object].
@DamagedOrganic it is, and enough so that a special symbol Symbol.toStringTag is defined to control this behaviour and the behaviour of what happens when you call Object.prototype.toString.call(myObject). I am not sure why V8 chose to use the constructor name here - Thanks for the interesting question.
Apparently, at the moment there's no way to control this behavior from Node.js runtime directly, so the answer above is exhaustive. Thank you.
@DamagedOrganic I am a maintainer of said Node.js runtime - if there is a legitimate need to customize this please open an issue! (or email me (my email is in the Node.js repo home page at GitHub) and I will if you'd rather not).
Also - happy to help with the actual question - always fun to dig in V8 and figure why they do stuff ^^

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.