So I have an object created by the eventemitter3 node module. The event emitter has a property _events, which, when I look at it in the Chrome inspector, shows Events {}. However, when I attempt to look at its constructor (temp1.constructor), I get undefined! Under the description on MDN (here), it says "All objects will have a constructor property", yet I have a clear counterexample here in my Chrome console (I also tried this in Firefox, so it's unlikely to be a problem with V8).
I can see in the eventemitter3 source the following lines that define the Events class:
//
// We try to not inherit from `Object.prototype`. In some engines creating an
// instance in this way is faster than calling `Object.create(null)` directly.
// If `Object.create(null)` is not supported we prefix the event names with a
// character to make sure that the built-in object properties are not
// overridden or used as an attack vector.
//
if (Object.create) {
Events.prototype = Object.create(null);
//
// This hack is needed because the `__proto__` property is still inherited in
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
//
if (!new Events().__proto__) prefix = false;
}
So I suppose it makes sense that they're trying to get rid of the constructor property but:
How does Chrome still know where the object came from enough to print its name in the console?
Is the MDN description wrong about constructors?
How can I glean the string "Events" from the object in JavaScript?
Related
When running the following code in the console:
console.dir(document);
In Chrome, I see, among other things:
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);
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.
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);
Working fiddle: https://jsfiddle.net/vL6smo51/1/
There's a few things to be aware of.
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.
Second, is that the browser has access to more than you might via the JS API directly in terms of interrogation.
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.
Fourth is that not all properties are iterable by design, but may still show up in the log (see #2 above)
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.
I am creating a scriptable application using the native windows API and the chakrart JavaScript engine.
I have interfaces like the following in my IDL:
[oleautomation, dual, nonextensible,
uuid(...)]
interface IMyInterface : IDispatch
{
[id(MYDISPID_EVENT), propget] HRESULT Event(BSTR strEventName, [out, retval] IDispatch ** ppHandler);
[id(MYDISPID_EVENT), propput] HRESULT Event(BSTR strEventName, IDispatch * pHandler);
};
I have been able to add the native objects to the chakrart engine without trouble (by using JsVariantToValue and then setting a property on the global object of the current context). However when I attempt to set an event handler I get the exception message 'Object doesn't support this property or method'.
I have tried the following syntax variations:
object.Event["foo"] = handler;
object.Event("foo", handler);
object.put_Event("foo", handler);
object.Event("foo") = handler;
That last is close to how this would be done using vbscript:
object.Event("foo") = GetRef("handler)
If I use method syntax in the IDL like the following it works, I would simply prefer to use property assignment if possible.
[id(MYDISPID_GETEVENT)] HRESULT GetEvent(BSTR strEventName, [out, retval] IDispatch ** ppHandler);
[id(MYDISPID_SETEVENT)] HRESULT SetEvent(BSTR strEventName, IDispatch * pHandler);
And also note that simple property assignments do work, it is only when I try indexed properties that it breaks. I do understand that JavaScript does something very different with object[prop] syntax, at least for native JavaScript objects. Is this perhaps simply an incompatibility in the chakrart interop layer?
I have confirmed a way to perform this with the desired syntax (App.Event['foo'] = someHandler;). As mentioned I already knew JavaScript treats name['foo'] as a member lookup on name
Have the App object implement a propget for Event that returns an object with a custom (non-dual interface) IDispatch implementation. That object keeps a map of names to DISPIDs and whenever an unknown name is requested via GetIDsOfNames a previously unassigned DISPID is returned.
Note that in order to comply with the rules for IDispatch the object needs to keep track of names even if a null handler is later assigned to a given name, so that the same DISPID will always be returned for a particular name.
In Chrome, try the following in the console. First
console = 0;
to assign the value 0 to console. Then
console // (prints `0`)
to check we have correctly overwritten console. Finally,
delete console
Surprisingly, console now holds the original Console object. In effect, the delete keyword "resurected" console, instead of exterminating it!
Is this expected behaviour? Where is this implemented in the Chromium code?
As mentioned in MDN's documentation on delete:
If the delete operator succeeds, it removes the property from the
object entirely, although this might reveal a similarly named property
on a prototype of the object.
Your delete simply unshadows native property inherited through prototype chain.
Some browsers have window inherit from native prototype and you'll have check out sources to see how property is inherited, if you really want to know that much details, but mostly they work just like JS' own.
Got it:
I've managed to prove the console is a property of the global object: just open your console and type: this.parent or window.parent. This will show a more complete list of properties and methods at your disposal. Including console: Console, about 2/3 of the way down, just below chrome: Object (interesting...:)). I thought of this when I remembered that I somehow managed to change the CSS rules of the console itself (in chrome, don't ask me how I got there, I can't remember). Bottom line: console ís a property of the window object. I think this backs up my explanation rather well.
#Randomblue: Since you're interested in how this is implemented in v8 you can check the trunk here, or browse the bleeding. Somewhere you'll find a test dir, that has a number of files that deal with delete. Special attention is given to delete used on global variables/properties: they can't be deleted, in other words: the console is never really gone. I would like to know why this answer went from being voted helpful and accepted to not-helpful and not-accepted, though...
It's perfectly simple. Console isn't some random, stand-alone, object. It's actually a property of the global object. Open your console and type this.console === console or window.console === console. It logs true, of course.
So thanks to implied globals console = 0 is pretty much the same as window.console = 0. You're sort of reassigning a property of an instance. The difference with normal objects is that the global object isn't just any old object: it's properties cannot be deleted (somewhere here on MDN). So your global is masking the console object, which is still there, you've just lost your reference too it:
var bar = window.console;
console = 12;
bar.log(console);//logs 12, bar is now an alternative reference to the console object
delete console;//unmasks the console reference
console === bar;//true
Don't, for a moment, be fooled into thinking the global object doesn't have a prototype. Just type this.constructor.name and lo and behold: Window with a capital W does appear. Another way of double checking is: Object.getPrototypeOf(this); or Object.getPrototypeOf(window);. In other words, there are prototypes to consider. Like always, the chain ends with Object.prototype:
Object.getPrototypeOf(Object.getPrototypeOf(window));
In short, there is nothing weird going on here, but the weird nature of the global object itself. It behaves as if there is some form of prototypal inheritance going on. Look at the global object as though it were set up like this:
this.prototype.window = this;//<-- window is a circular reference, global obj has no name
this.prototype.console = new Console();//this is the global object
this.hasOwnProperty(console);//false
console = 0;//implied global
When attempting to access console, JS finds the property console you've just set prior to the instance of the Console object, and happily returns its value. The same happens when we delete it, the first occurance of console is deleted, but the property higher up the prototype chain remains unchanged. The next time console is requested, JS will scan the inheritance chain and return the console instance of old. The console-object was never really gone, it was merely hidden behind a property you set yourself.
Off topic, but for completeness' sake:
There are a few more things too it than this (scope scanning prior to object/prototype chain searching), due to the special character of the global object, but this is, AFAIK, the essence of it.What you need to know is that there is no such thing (in JS) as an object without (at least) 1 prototype. That includes the global object. What you're doing merely augments the current global object's instance, delete a property and the prototype takes over again. Simple as that. That's what #Peeter hinted at with his answer: implied globals are not allowed in strict mode, because they modify the global object. Which, as I tried to explain here, is exactly what happens here.
Some properties of the window object aren't deletable. True is returned because you aren't running in strict mode. Try the following (not in console):
"use strict";
delete console;
and you will get an exception (JSFiddle).
You can read more about how this is handled at http://es5.github.com/#x11.4.1
First, this is not just the console, you can do this with every native property every browser-defined property on window.
setTimeout = 0;
setTimeout //=> 0
delete window.setTimeout;
setTimeout //=> function setTimeout() { [native code] }
Properties that are part of the ECMA-Script Spec can be fully overwritten & deleted:
Array = 0;
Array //=> 0
delete window.Array;
Array //=> ReferenceError
You can nearly overwrite any property on window, delete the overwrite and get back to the normal function.
The simple reason for this is that console and all the other native global functions browser defined properties are not linked to the DOMWindow Object via javascript but via C++. You can see the console being attached to the DOMWindow right here and the implementation of DOMWindow here
That also means that the window object is somehow a C++ Object masked as a javascript object the window object is at least partly defined by C++, and it is not prototypical inheritance doing the magic: Take for example:
window.hasOwnProperty('console') //=> true, console is defined directly on the window
window.__proto__.hasOwnProperty('console') // => false, the window prototype does not have a console property
Also, if it was prototypical inheritance, the following would lead to console returning 3:
window.__proto__.console = 3;
delete console;
console //=> still returns console;
window.hasOwnProperty('console') //=> the window still has it.
The same with a property respecting prototypical inheritance:
window.someProp = 4;
window.__proto__.someProp = 6;
someProp //=> 4
delete someProp;
someProp //=> 6
Therefore, when you set console to anything, it is gone and can only be resurrected by (hoorray for the irony): delete console.
So, what it means is that you cannot delete any native properties on the window object. Try to delete window.console when it is not overwritten, it will just pop up again. The fact that you are able to overwrite it in the first place (even in strict mode) without receiving any kind of warning (in my eyes) one of the key vulnerabilities of javascript (set setTimeout on nearly any page to 0 and see it tear itself apart), but as they say in spiderman:
With great power comes great responsibility
Update
To include a hint that this is specific to the implementation of the browser / engine and not any requirement of the language itself: In nodejs, deleting both engine-specified properties and ecma-script properties on the global object works:
delete this.console //=> true
console //=> ReferenceError
delete parseInt //=> true
parseInt //=> ReferenceError
The exact same thing happens in Firefox.
I'm assuming the following, based on observations of my own.
Variables are first checked to see if they match local variables, if not, then it will be checked to see if they match window.variable.
When you set console to 1, you set the local variable console to 1, so any lookups will see that instead of window.console (which still exists). When you delete console the local variable console gets deleted. Now any lookups of console will match window.console. That's why you get the behaviour you get.
I am assuming this based on experimenting with the JavaScript interpreter in Firefox.
And, I'm sorry about incorrect terminology (feel free to edit), I'm not that experienced with namespaces.
The delete operator removes a property from an object.
...
You can use the delete operator to delete variables declared
implicitly but not those declared with the var or the function
statement.
See delete on MDN
Edit:
See also Understanding delete if you're really into hardcore JavaScript.
What happens is you are overwriting the objects prototype, then you delete the overwritten value and what is left... is the original object, which is it's prototype.
Expected behavior. Little known fact that the Javascript console does not run in the browser's global space, but rather it runs within its own anonymous function.
I know that different browsers handle things differently, but in short -- delete does not operate as expected because it isn't operating in the global space.
If you really want to see things break, try playing with delete window.console
Ok, it is official -- I'm an idiot. One of the new features in ECMAScript is the ability to declare a property as dontdelete. Sorry about that confusion.
I was browsing MDC about new functions added to Object. One of them, Object.preventExtensions, is said to prevent mutations to the object's prototype, which can be obtained by using Object.getPrototypeOf or __proto__.
On Chrome, however, it seems to simply allow mutations to the object's prototype. This can be confirmed by just executing the code on the relevant page:
// EXTENSION (only works in engines supporting __proto__
// (which is deprecated. Use Object.getPrototypeOf instead)):
// A non-extensible object's prototype is immutable.
var fixed = Object.preventExtensions({});
fixed.__proto__ = { oh: "hai" }; // throws a TypeError
I don't get this TypeError, and fixed.__proto__.oh === 'hai', so it has been set even though it should have been disallowed. I can also add it when coding like Object.getPrototypeOf(fixed).oh = 'hai'.
Does this mean Chrome has a different interpretation of this function? How can one prevent extensions to an object's prototype (in Chrome)?
Nope, Chrome and Mozilla both implement the standards part of the spec the same. Read carefully:
Object.preventExtensions only prevents
addition of own properties. Properties
can still be added to the object
prototype.
Everything to do with .__proto__ is non-standard, and Chrome can implement that differently. You showed only that Chrome implements details with .__proto__ differently, and in my opinion, more intuitively: The spec says that the prototype is still extensible, so it makes sense that you should still be able to mutate it. The question then becomes why did Mozilla implement it that way?
For example, the following code works the same on both Chrome and FF:
var fixed = Object.preventExtensions({});
Object.getPrototypeOf(fixed).p = 99;
fixed.p; // 99
Clearly the prototype is still mutable. That makes sense with Chrome's implementation of .__proto__.
So to prevent extensions of a prototype, do so separately:
var fixed = Object.preventExtensions({});
Object.preventExtensions(Object.getPrototypeOf(fixed));
Object.getPrototypeOf(fixed).p = 99; // TypeError: Can't add property p, object is not extensible
In ECMAScript 5, objects have a boolean internal property named [[Extensible]], this property is set to false when you call the Object.preventExtensions method, and after that, no new own properties can be added to the object.
On Chrome 14.0.786.0, the assignment to __proto__ throws a TypeError as you expect.
But remember that the __proto__ extension is non-standard, so it's behavior may vary, of course syntactically it is a "property assignment", but internally it doesn't "add an own property", it mutates the object's prototype, thing that is not possible to do by any standard method.
About the example of the Object.getPrototypeOf method you show us, it's simply retrieving the object's prototype, in the case of your fixed object, it's the Object.prototype object:
Object.getPrototypeOf(fixed) === Object.prototype; // true
So your example:
Object.getPrototypeOf(fixed).oh = 'hai'
Is equivalent to:
Object.prototype.oh === 'hai';
When you run:
window.toString.call("")
everything's fine in FF/CH but in IE8 you get a script error. Investigating a bit more it turned out, that window.toString.call is undefined in IE8?
You can also run this one:
window.toString instanceof Function;
// false
alert(window.toString);
// function toString() {
// [native code]
// }
Why is that and how to solve it? And I started wondering how come jQuery works in the first place?
window is a host object, and the ECMAScript Language Specification (3rd edition) does not require host objects to be derived from the native Object object. In IE (and probably in some other browsers) host objects aren't, so they don't support any of the native methods or properties (although they may have methods or properties with the same names as native methods or properties which are accessible to scripts).
If all you want is to get hold of the language implementation's default native toString method then you should use Object.prototype.toString.call("").
NickFitz is correct, the toString method on the host object that you are finding is purely so that if you did
alert(window);
you would get the text [object]
All that the javascript method toString() used in your examples would acheive is to make a string from a string so the correct way to do what you are trying is;
var a =new String ("");
or simply
var b = "";
or if you really want to be silly;
var b = "".toString();