Checking if property exists in object in JavaScript (browser compliance) - javascript

What is the most standards and best browser compatibility for checking if a given property of an object exists in JavaScript?
I can think of the following:
1.
if(window && 'navigator' in window && 'userAgent' in window.navigator) {}
2.
if(window && window.navigator && window.navigator.userAgent) {}

The window && check at the start of each of the checks is unnecessary. There's never a natural case where that will evaluate to false. (If you run the code in Nodejs or in a web worker where the window global isn't present, then the line will throw an exception, and not evaluate to false. There's no environment where window && will improve things, and having that there is misleading.)
#1 will check whether the window object has a navigator property with any value, etc. #2 will check whether the window object has a navigator property with a truthy value. Unless you expect window.navigator or window.navigator.userAgent to be present as properties but be set to false, null, undefined, 0, NaN, or '' (which those specific properties aren't ever naturally in browsers), then #2 will work just fine and it's nicely shorter.

Both are completely standard and fully supported. Go with 2 because it is the most commonly used, is shorter, and (not that it matters) was supported since the inception of javascript, whereas 1 came along in ES3.
See MDN for more details:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors

Related

What does $.support.placeholder mean in jQuery?

I don't understand this condition: $.support.placeholder
Can anyone explain to me what this is for?
if ($.support.placeholder) {
alert("Testing");
} else {
return;
}
It's checking to see if the placeholder property on the $.support object is truthy¹ and, if so, it does the alert; if not, it does a return, exiting the function this code is in.
jQuery's support object (long deprecated, it shouldn't be used anymore) tells you what the current browser supports. From the link:
A collection of properties that represent the presence of different browser features or bugs. Intended for jQuery's internal use; specific properties may be removed when they are no longer needed internally to improve page startup performance. For your own project's feature-detection needs, we strongly recommend the use of an external library such as Modernizr instead of dependency on properties in jQuery.support.
(my emphasis)
In this case, the check is seeing if the browser supports the placeholder attribute on input elements. But see above, the check is unreliable. If jQuery no longer needs to know this information internally, the property may be removed, and that code would be tricked into thinking the browser didn't support placeholder (which nearly all do) because getting the value of a property that doesn't exist results in undefined, which is falsy.
In fact, the current version of jQuery as of this writing (v3.3.1) doesn't have placeholder:
console.log("typeof $.support.placeholder:", typeof $.support.placeholder); // "typeof $.support.placeholder: undefined"
var input = document.createElement("input");
input.type = "text";
var supportsPlaceholder = "placeholder" in input;
console.log("supportsPlaceholder: ", supportsPlaceholder); // "supportsPlaceholder: true" (in nearly all browsers)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
The code in the question would incorrectly report that your browser doesn't have placeholder support even when it does.
¹ a truthy value is a value that coerces to true when used as a boolean, such as in an if. A falsy value is one that coerces to false. The falsy values are null, undefined, 0, NaN, "", and of course, false; all other values are truthy.

Why is document.all defined but typeof document.all returns "undefined"? [duplicate]

This question already has answers here:
Why is document.all falsy?
(4 answers)
Closed 6 years ago.
I was doing some research on JavaScript's typeof operator, and stumbled across the following weirdness:
Exceptions
All current browsers expose a non-standard host object document.all with type Undefined.
typeof document.all === 'undefined';
Although the specification allows custom type tags for non-standard exotic objects, it requires those type tags to be different from the predefined ones. The case of document.all having type tag 'undefined' must be classified as an exceptional violation of the rules.
(Source)
I set up the following in my browser to test it:
console.log("typeof: " + typeof document.all);
console.log("toString: " + document.all);
It yielded:
typeof: undefined
toString: [object HTMLAllCollection]
So why is document.all like this? Since document.all is an object (defined in my browser), shouldn't typeof return "object", not "undefined"?
Take a look at this one from Html Spec
The all attribute must return an HTMLAllCollection rooted at the
Document node, whose filter matches all elements.
The object returned for all has several unusual behaviors:
The user agent must act as if the ToBoolean abstract operation in JavaScript returns false when given the object returned for all.
The user agent must act as if the Abstract Equality Comparison algorithm, when given the object returned for all, returns true when
compared to the undefined and null values. (Comparisons using the
Strict Equality Comparison algorithm, and Abstract Equality
comparisons to other values such as strings or objects, are
unaffected.)
The user agent must act such that the typeof operator in JavaScript returns the string "undefined" when applied to the object returned for
all.
The third case is yours.
The rationale for this is compatibility with code designed for old browsers, as explained in a note in the specification:
This violation is motivated by a desire for compatibility with two classes of legacy content: one that uses the presence of document.all as a way to detect legacy user agents, and one that only supports those legacy user agents and uses the document.all object without testing for its presence first.
Hope it will make sense for you.
A workaround update:
document.all !== undefined;
>> true /*otherwise*/ false
Since https://html.spec.whatwg.org/ requires, that...
The user agent must act as if the Abstract Equality Comparison algorithm, when given the object returned for all, returns true when compared to the undefined and null values.
but since...
(Comparisons using the Strict Equality Comparison algorithm, and Abstract Equality comparisons to other values such as strings or objects, are unaffected.)
the use of Static Type Comparison Operator ( e.g.: === | !== ) is perfectly safe to check if the HTMLAllCollection object is usable and\or present in the current UA client.
The Dynamic Type Comparison Operator will however continue to return a false absence as required by specification.
document.all != undefined;
>> false /*otherwise*/ false
A workaround (older)
"all" in document;
>> true /*otherwise*/ false
A more elaborate approach when dealing with third party code would be
delete document.all && "all" in document
>> true /*otherwise*/ false

Why does the delete keyword act opposite to expected?

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.

typeof returning "unknown" in IE

I have a window, where before being closed I refresh the underlying page.
if(opener && typeof(opener.Refresh) != 'undefined')
{
opener.Refresh();
}
If I moved away from the original opening page, this code would throw a "Permission Denied" error.
Debugging the code revealed that typeof(opener.Refresh) was equal to "unknown" instead of the expected "undefined".
As far as I'm aware "unknown" is not one of the return values for typeof, so how and why would this value be returned?
Further Information
I avoided the error by changing the check to:
if(opener && typeof(opener.Refresh) == 'function')
However examples like this (detecting-an-undefined-object-property-in-javascript) do not seem to factor "unknown" into the equation.
According to a duplicate question at Bytes, the typeof value unknown is added to JScript version 8, along with date.
A comment to a blog by Robert Nyman can also be explanatory:
Internet Explorer displays “unknown” when the object in question is on
the other side of a COM+ bridge. You may not know this or realize
this, but MS’s XMLHTTP object is part of a different COM+ object that
implements IUnknown; when you call methods on it, you’re doing so over
a COM bridge and not calling native JavaScript.
Basically that’s MS’s answer if you try to test or access something
that’s not a true part of the JScript engine.
Try in operator. I had the same problem (with applet) and I solved it using in:
if("Refresh" in opener) {
opener.Refresh();
}
The ECMAScript specification states that for host objects the return value of the typeof operator is:
Implementation-defined except may not be "undefined", "boolean",
"number", or "string".
I believe the unknown value is only ever returned in Internet Explorer. Interestingly, MSDN does not mention it:
There are six possible values that typeof returns: "number," "string,"
"boolean," "object," "function," and "undefined."

Why is comparing the ".constructor" property of two windows unreliable?

I'm trying to understand why the following is not working as I would think it does. I'd like to check whether an object is a window. I was thinking that checking the constructors of the current window and another window would work.
So, first creating another window:
var popup = window.open('', '', '');
And then checking with:
popup.constructor === window.constructor;
But for some reason the results vary among browsers:
IE7: Returns true
But that's simply because .constructor === undefined
IE8: Returns false
IE9: Throws an error with no text ("")
Chrome: Returns false
Though in both cases .constructor === DOMWindow - but DOMWindow is not accessible directly like this
Opera: Returns false
Though in both cases .constructor === Object
Firefox: Returns false
Though in both cases .constructor === Window
Why isn't this reliable and working correctly? jQuery simply checks for "setInterval" in window, but I'd like to create a more robust function for checking whether an object is a window.
Every separate window is a separate global context for the interpreter (and yes I'm ignoring IE7 for the moment). Your issue really becomes clear when you think about instanceof:
if (something instanceof Array) { ... }
The problems with that occur when "something" is a value that was constructed in a separate context, like an iframe for example. Each window has it's very own copy of the "Array" constructor, so even though an object may be an Array instance, it's not an instance of the same constructor function.
I suspect that the IE7 behavior is related to some other weirdness that I think is something like window.window being not equal to window. Like, one of them is in fact a reference to something internal. (Maybe it's window not being equal to window.self even though they otherwise behave identically. I don't invest a lot of philosophical thought time in contemplating IE7.)

Categories

Resources