I'm looking to (dynamically) obtain a list of HTML elements the browser is currently aware of, such as HTMLPreElement, HTMLSpanElement etc. These objects are global, i.e.
console.log('HTMLPreElement' in window); //=> true
So I thought I'd be able to use getOwnPropertyNames like this:
console.log(Object.getOwnPropertyNames(window));
to obtain the full list of global properties (MDN states that this returns both enumerable and non-enumerable properties).
Using the above, I get an array with around 70 property nanes. But, it doesn't include objects like HTMLPreElement - only HTMLElement. I also tried:
console.log(Object.getOwnPropertyNames(window.Window.prototype));
which brings back a bigger list (including addEventListener etc) but again, no HTMLPreElement.
So, where the heck do these HTML{Tag}Element objects reside?
for (var prop in window)
console.log(prop);
That's what you need?
In Firefox, it seems to be the behavior of elements that their global object is not added unless explicitly requested as a global variable or property. Perhaps Firefox lazy loads them into the environment so that they don't consume memory unless they're actually needed.
It seems that they do not show up when simply requesting the keys of the global object via Object.getOwnPropertyNames unless they've first been explicitly referenced as described above.
http://jsfiddle.net/mBAHm/
var obj = window;
while(obj){
for(let prop of Reflect.ownKeys(obj)){
console.log(prop);
};
obj = Object.getPrototypeOf(obj);
};
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.
If you create a regular javascript object using say var obj = {}; it will have the object prototype. Same goes for objects created using var obj = new MyClass(); Before Object.create was introduced there was no way around this. However nowadays it's possible to create an object with no prototype (respectively null as its prototype) using var obj = Object.create(null);.
Why is this important? What advantages does it bring? Are there any real world use cases?
It's a completely empty object (nothing inherited from any .prototype including Object.prototype), so you can be guaranteed that any property lookup will only succeed if the property was explicitly added to the object.
For example, if you want to store some data where you won't know the keys in advance, there's a possibility that a provided key will have the same name as a member of Object.prototype, and cause a bug.
In those cases, you need to do an explicit .hasOwnProperty() check to rule out unexpected inherited values. But if there's nothing inherited, your testing can be simplified to a if (key in my_object) { test, or perhaps a simple truthy test if (my_object[key]) { if appropriate.
Also, with no prototype chain, I would imagine that lookups of properties that turn out to not exist would be faster, since only the immediate object would need to be checked. Whether or not this pans out in reality (due to optimizations) would be determined with performance testing.
The only difference here between creating an object with {} and Object.create(null) is that the Object prototype will not be inherited from.
This means that any of the methods we typically assume we can call on any object will not exist, such as toString and valueOf. A list of these can be found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
From a performance perspective, creating an object simply by {} is actually much faster, so unless you specifically cannot have the functions under the Object prototype you should not create objects in that manner.
http://jsperf.com/null-vs-empty-object-performance
This question already has answers here:
Why is it possible to query jQuery('div') like an array?
(5 answers)
Closed 9 years ago.
Just like $ itself is not simply a user defined object (it is, at its base, a function and that is why you can use the () operator on it and it inherits all the functions from Function.prototype) what is the actual Javascript type of the so-called jQuery object?
It has to be one of the standard array-like objects because you can use the [] operator on it, something you can't even do on strings in IE8 (and no, you can't, not on the Object String. You can on string literals before they're boxed).
So again, its not a user defined object (NB there's no such thing as a jQuery object, its written in raw javascript somewhere along the line). I have a feeling its a NodeList (or equivalent in browsers that don't have NodeList).
A reason you might have noticed this is you've added methods to the Array prototype, and you discover the function doesn't exist on a set of nodes found by jQuery!
So is anyone intimate with jQuery enough to share this lower level info with me? Or is there a short, exhaustive list of array-like objects in Javascript that I can test the jQuery object against till I find out which one it is myself?
The reason I ask this, instead of assume that jQuery is just adding elements to the object, using indexes as property names (I.e. obj[0] = x;) is because of how the Chrome dev tools and Firebug display them.
This shows dev tools displaying Array, jQuery and Array-like objects, and that there is at least some magic behind what jQuery is doing.
EDIT
It's not a NodeList. If it helps; inspecting it in Chrome shows that it is of type jQuery.fn.jQuery.init[0] with a __proto__ describing itself as Object[0] (which has all the jquery methods). Anyone explaining what and how jQuery is doing would be greatly thanked. I'm going to look at jQuery's source now.
And as for the image, it isn't simply an overridden .toString().
$('').toString == Object.prototype.toString //true
$('').toString == Array.prototype.toString //false
What is a jQuery object?
The $() or jQuery() function returns a jQuery object. It is not an Array and not a NodeList, and not in any way derived from either of those. It is an Object created with the jQuery constructor (which is actually the jQuery.fn.init() function). This object is very much like any other Object created with new SomeConstructor(), or even a plain object literal.
When you call $() or jQuery() without a new operator, it automatically does a new for you:
jQuery = function( selector, context ) {
// The jQuery object is actually just the init
// constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
},
The jQuery object is somewhat "array-like" in that it has properties with numeric indexes and a .length property, but these are just properties set directly in the object in the jQuery code. It also has a number of properties and methods that come from its prototype, which is jQuery.fn or jQuery.prototype (jQuery.fn is merely a reference to jQuery.prototype; they are the same object.)
You can see an example of this in the jQuery code that wraps a DOM element when you use $(element):
// HANDLE: $(DOMElement)
} else if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
} ...
In this bit of code, selector is actually a DOM element, and the code stores that element in this[0] and sets this.length to 1. This is why you can use [0] and .length, simply because the object has these properties that were explicitly set.
Why does a jQuery object display like an Array?
There is some magic involved here, but the magic is in Firebug and the Chrome Developer Tools. They both look for two specific properties to decide to display an object as if it were an array:
A length property which is either a number or a string containing a number.
A splice property which is a function.
If the object has both those properties, it is displayed as an array.
You can test this by pasting the following line into either the Chrome DevTools or Firebug:
({ splice:function(){}, length:0 })
Chrome displays:
[]
Firebug displays:
Object[ ]
Remove either property and it will display as an object.
It doesn't matter if these properties are in the object itself or its prototype. You can paste this code into either console and it will display the same array-like notation as the code above:
function A(){}; A.prototype = { splice:function(){}, length:0 }; new A;
You can add an element to your "array":
({ splice:function(){}, length:1, 0:'test' })
Now Chrome displays:
["test"]
and Firebug displays:
Object[ "test" ]
Note that the Firebug display is not exactly the same as what it displays for an actual Array. Firebug puts that Object prefix in front of the array-like display to let you know that it's not an actual Array.
It does that on jQuery objects too. If you enter:
$(document.body)
Firebug displays:
Object[ body ]
You can click the word Object to get the full object-style display of the jQuery object instead of the array-like display.
Chrome doesn't make this distinction; it displays an Array or an array-like Object the same way.
So there's the magic. It's really nothing special in jQuery, except that the object it returns does have the length and splice properties. The splice() method is on the prototype and the length property may come from the prototype (for an empty "array") or more often from the jQuery object itself.
Those two properties trigger the real magic: a hack in the DevTools and Firebug to try to provide a more useful display for objects like these.
Bragging rights :-)
A bit of historical trivia: I was actually responsible for this part of jQuery's architecture. The very first version of jQuery in early 2006 didn't work like this. The jQuery object didn't have a .length and [0], [1], etc. properties. Instead it had a "private" array of its own, and you had to use .get(n) to fetch values from that array.
I thought it would be more convenient to have direct array-like access to the elements and came up with the idea of setting the .length, [0], [1], etc. properties so it would work like a read-only array.
my understanding is a sub class of Array, which inherited from Array Class and has its own method
changes above:
it's not sub class of Array. But it made from Array, but after Array object created, jQuery alter the object prototype with jQuery its own prototype,then finally it become jQuery object which is Array-like object.
console.log(Object.prototype); // -> "{}"
I expected console.log(Object.prototype) to output some properties or mothods like "toString".
Why it doesn't?
The default methods are not marked as enumerable and thus not shown when viewing Object.prototype.
The reason for this is pretty simple: When viewing some object or more important iterating over its properties using for(var prop in object) you usually do not want to see any of the default methods. Of course one could always use a obj.hasOwnProperty(prop) check in such a loop but it's incredibly comfortable not having to do this by knowing that there's nothing enumerable on Object.prototype by default (and good code doesn't add enumerable properties to it either).
What are expando objects in javascripts?
For what purpose we need this ? Any complete example will be appreciated
I found 1 article here Javascript: The red-headed stepchild of web development
Well, in javascript, any object is an expando object. What it means is, as the article covers, that whenever you try to access a property1 it will automatically be created.
var myObj = {}; // completely empty object
myObj.myProp = 'value';
The moment you assign myProp a value, the property myProp is dynamically created, eventhough it didn't exist before. In a lot of other languages, such as C#, this is not normally possible (actually C# has just enabled expando object support as well, but that's besides the point). To access a property in a normal class in C#, you need to specify in the class that it does indeed have this property.
1 Not quite correct. See npup's comment below for clarification.
Everything except primitive types(string, number,boolean) are objects and support Key:values structure. properties(keys) can be accessed and set using the dot notation as well as the square brackets.
var myObj = {};
myObj.myProp1 = 'value1'; //works, an expando property
myObj[myProp2] = 'value2'; // doesn't work, myProp2 is an undefined name.
myObj['myProp2'] = 'value2'; // works , an expando property
myObj[2010]= 'value'; //note the key is number, still works, an expando property??
myObj.2010 = 'value'; // FAILS. to use dot notation, key must be a string
An article written in 2007 that uses document.all (as the only way to access elements)? That's a big red flag.
It is just dressing up "You can add properties to an object" with some buzzwords.
We need to be able to do this because otherwise we wouldn't be able to store data, and that would make JavaScript a pretty useless language.
(Everything is an array? No it isn't. And it iterates over an object without a hasOwnProperty wrapper. That isn't safe. Just keep away from the article, it is worse than useless)
JavaScript turns elements with specific IDs of names into expandos of the returned DOM object. It is explained here.