17

When running the following code in the console:

console.dir(document);

In Chrome, I see, among other things:

enter image description here

This seems to imply that the domain property is directly on the document object. However, it isn't.

console.log(document.hasOwnProperty('domain'));

In Chrome 72, going up the prototype chain, it appears to be on Document.prototype:

console.log(Document.prototype.hasOwnProperty('domain'));
console.log(Object.getOwnPropertyDescriptor(Document.prototype, 'domain'));

(In FF 56 and perhaps some other browsers, it appears to be on HTMLDocument.prototype instead)

As you can see from the snippet, the property is actually composed of a getter and a setter. But, I was under the impression that getters are shown in the console as (...), like in this image, which you had to click on the (...) to invoke the getter.

If I create a similar object, one whose prototype contains a getter/setter property, and I log the object, the getter does not get invoked while examining it:

// look at results in Chrome's browser console, not snippet console
class theProto {
  get foo() {
    return 'fooVal';
  }
  set foo(arg) {
    // do something
  }
}
class theClass extends theProto {
}
const instance = new theClass();
console.dir(instance);

enter image description here

The same sort of behavior can be seen for many properties on document. For example, all of the other properties you can see in the first screenshot also appear to be getters/setters on one of the prototype objects, and none of them are on document itself:

console.log(
  ['dir', 'doctype', 'documentElement', 'documentURI', 'embeds', 'fgColor', 'firstChild', 'firstElementChild']
  .some(prop => document.hasOwnProperty(prop))
);

You can also see this on window properties, as well as elements. This happens in FF as well.

const input = document.createElement('input');
// console.dir(input);

// but the own property list is empty!
console.log(Object.getOwnPropertyNames(input));
<img src="https://i.stack.imgur.com/R5u3S.png">

Is it possible to create an object with the same logging behavior as these, where console.diring an object will also invoke any getters in the prototype chain immediately, instead of displaying (...)? How would I modify my theClass snippet? Or, do certain predefined objects (like DOM objects) just have an exception to the normal logging behavior?

I know how to invoke the getters programmatically, I'm just curious about the seeming inconsistency.

Snow
  • 3,820
  • 3
  • 13
  • 39
  • Actually, if you look up at the `__proto__` chain of `document`, you will find the `domain` getter and setter with the `(...)` evaluation. I think the value you are seeing there is **not** the `domain` attribute of the `Document` interface, as you can see in this specification: https://html.spec.whatwg.org/multipage/dom.html#document, but rather the DOM element's attribute, which gets somehow enumerated. In a nutshell, it sounds, to me, that such property specifically is rather an attribute evaluated by `console.dir` rather than the `Document` interface's attribute value. – briosheje Feb 20 '19 at 10:42
  • 1
    The only reasonable way I see to actually do such is overriding the property in `theClass`, by evaluating its value with a self invoking anonymous function. This will lead to the same result as `document` as you mentioned, resulting in an object having `foo` evaluated and a `foo` getter and setter in its ancestor. https://jsfiddle.net/vL6smo51/ . The only logical condition I see for that to happen is that `Document` is evaluating some properties of `HTMLDocument`, hence the values. – briosheje Feb 20 '19 at 10:51
  • related: [Chrome Developer Tools Invoke Property Getter](https://stackoverflow.com/questions/45968614/chrome-developer-tools-invoke-property-getter) – Bergi Feb 20 '19 at 12:02
  • 1
    This appears to be [**a chrome devtools bug**](https://bugs.chromium.org/p/chromium/issues/detail?id=915867). Weird though that you can reproduce it in FF as well… – Bergi Feb 20 '19 at 12:07
  • @Bergi That issue looks to be *somewhat similar*, but it's not the same thing - there, the getter is being shown on all levels of the prototype chain, which is different from the getter being invoked automatically. – Snow Feb 22 '19 at 10:14
  • What we're seeing may be deliberate in both browsers, because being able to see all of these properties on DOM objects when logging them is *useful* (in contrast to having to click on every getter manually), but that's just a guess. – Snow Feb 22 '19 at 10:14
  • @Snow Yeah, for builtin properties the browser might know that it can evaluate the getters without causing side effects, and it just wants to be helpful. – Bergi Feb 22 '19 at 10:38
  • It doesn't look like there are going to be any sufficient answers before the bounty expires. If someone posts a good answer in the future, I'll be happy to reward them a new +300 bounty. – Snow Mar 01 '19 at 05:36

3 Answers3

1

Is it possible to create an object with the same logging behavior as these, where console.diring an object will also invoke any getters in the prototype chain immediately, instead of displaying (...)?

Thecnically, yes, despite it will not exactly evaluate the getters (I'm not sure whether the values you see are evaluated when you console.dir them). You need to evaluate the property. However, using hasOwnProperty will return true.

// look at results in Chrome's browser console, not snippet console
class theProto {
  get foo() {
    return 'fooVal';
  }
  set foo(arg) {
    // do something
  }
}
class theClass extends theProto {
   foo = (() => theProto.prototype.foo)(); // or, really, just (() => this.foo)();
}
const instance = new theClass();
console.dir(instance);

enter image description here

Working fiddle: https://jsfiddle.net/vL6smo51/1/

briosheje
  • 7,356
  • 2
  • 32
  • 54
  • 1
    "*I'm not sure whether the values you see are evaluated when you console.dir them*" - no, it is evaluated when you instantiate `theClass` and initialises the `foo` own property. – Bergi Feb 20 '19 at 11:58
  • @Bergi I was refering to the `document` example he posted, not the example with `theClass` he provided. I'm aware that with that example specifically (theClass) console.dir does not evaluate properties, I was wondering whether it was with `document`, since it's just weird. – briosheje Feb 20 '19 at 12:25
  • 2
    This is effectively just manually invoking the getter in the constructor, and putting the result value right on the instance, so the fact that the logged object shows the result value on the instance isn't surprising at all. Though, one thing this snippet *does* show (if you check the Babel box) is that StackOverflow's transpiler looks not to be faithfully transpiling the class field, it looks like the right-hand side of `foo =` just gets lost? Wonder what's going on. – Snow Feb 22 '19 at 09:33
0

There's a few things to be aware of.

  1. First is that the console.* statements (other than .error) are async, you are passing the document object by reference that may in fact change between passing it to console.FN and the actual output to the log window.
  2. Second, is that the browser has access to more than you might via the JS API directly in terms of interrogation.
  3. Third is that document itself may have an inheritance chain that includes things like domain which is not a part of the document instance itself.
  4. Fourth is that not all properties are iterable by design, but may still show up in the log (see #2 above)
Tracker1
  • 19,103
  • 12
  • 80
  • 106
  • i think you mean "enumerable" or "accessible", not "iterable". – Bergi Feb 25 '19 at 20:13
  • can you expand on #3, please? – Bergi Feb 25 '19 at 20:14
  • While it's true that logged objects can appear differently due to being changed asynchronously, that's not what's going on here, see https://jsfiddle.net/c5npxr7z/1/ (you can open and close the logged object multiple times, but it never gains an own property of `domain`). – Snow Feb 25 '19 at 23:44
  • 1
    2. Good idea, the browser looks to be carrying out an *internal* routine when the object is logged, see https://jsfiddle.net/0xsg3z48/2/ - the custom getter on `Document.prototype` is never invoked when logging `document`! But, if you `delete` the property on the prototype, the associated property does not appear when logging `document`. So, it's probably impossible to emulate this behavior with a custom object. – Snow Feb 25 '19 at 23:45
-3

document.hasOwnProperty() statement only returns true if the object has the specified property as its own property (as opposed to inheriting it). In this cause domain property is inherited from HTMLDocument object. HTMLDocument is the prototype of document. So it returns false.