New Host Object Primer

T

Thomas 'PointedEars' Lahn

David said:
I have posted a new primer related to host objects and feature
detection/testing.

http://www.cinsoft.net/host.html

I think that about sums up the progress (and lack thereof) of the last
few years.

ISTM the RegExp is borken:

var reFeaturedMethod = new RegExp('^function|object$', 'i');

It matches case-insensitive either "function" at the begin of input or
"object" at the end, when it should match case-insensitive an input that is
either "function" or "object":

var reFeaturedMethod = new RegExp('^(function|object)$', 'i');

Furthermore, AISB,

return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');

becomes more efficient when writing

return !!(t == 'unknown' || (reFeaturedMethod.test(t) && o[m]));

The double negation to cast to boolean is a matter of taste; I do not think
it is necessary, because one possible result is a boolean already and the
other has been proven by this that it can be used in a type-converting
test.

Either the identifier or the code of isHostObjectProperty() does not make
sense.

I am going to comment on the rest later.


PointedEars
 
D

David Mark

Thomas said:
ISTM the RegExp is borken:

var reFeaturedMethod = new RegExp('^function|object$', 'i');

It matches case-insensitive either "function" at the begin of input or
"object" at the end, when it should match case-insensitive an input that is
either "function" or "object":

You got it. That reflects either sloppiness or cluelessness on my part
at the time I wrote that line (first one I wrote for My Library). :)
it's fixed in the pasted example and the library. Thanks!

And BTW, I forgot to mention your isMethodType as the inspiration for
that function. I'll add that when I have a chance. I need to explain a
little bit more about that one anyway.
var reFeaturedMethod = new RegExp('^(function|object)$', 'i');

Furthermore, AISB,

return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');

becomes more efficient when writing

return !!(t == 'unknown' || (reFeaturedMethod.test(t) && o[m]));

Could be. I'm not sure.
The double negation to cast to boolean is a matter of taste; I do not think
it is necessary, because one possible result is a boolean already and the
other has been proven by this that it can be used in a type-converting
test.

I just like for the function to return a boolean for all cases.
Either the identifier or the code of isHostObjectProperty() does not make
sense.

The identifier is not particularly descriptive. Read the description
carefully. There is an assertion you must make as part of the contract
with that (and isHostMethod). Remember that some objects (e.g.
childNodes) can have typeof "function". You are asserting that you will
do something other than call it, which is why the "unknown" types are
excluded. I need to flesh out the explanation, which was copied
straight from the skeletal library documentation.
I am going to comment on the rest later.

Comments welcome! :)
 
D

David Mark

David said:
You got it. That reflects either sloppiness or cluelessness on my part
at the time I wrote that line (first one I wrote for My Library). :)
it's fixed in the pasted example and the library. Thanks!

And BTW, I forgot to mention your isMethodType as the inspiration for
that function. I'll add that when I have a chance. I need to explain a
little bit more about that one anyway.

Now mentioned, as well as the blog post that followed Peter's site.
 
G

Garrett Smith

Thomas said:
David said:
I have posted a new primer related to host objects and feature
detection/testing.
[...]


ISTM the RegExp is borken:

var reFeaturedMethod = new RegExp('^function|object$', 'i');

It matches case-insensitive either "function" at the begin of input or
"object" at the end, when it should match case-insensitive an input that is
either "function" or "object":

var reFeaturedMethod = new RegExp('^(function|object)$', 'i');

A Literal would be shorter and would stay cached:

/^(?:func|obj)/;
Furthermore, AISB,

return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');

becomes more efficient when writing

return !!(t == 'unknown' || (reFeaturedMethod.test(t) && o[m]));

The double negation to cast to boolean is a matter of taste; I do not think
it is necessary, because one possible result is a boolean already and the
other has been proven by this that it can be used in a type-converting
test.
Double negation on a boolean is pointless. However, `o[m]` should not be
a boolean; it should be a function or an object.

Caveats:
Object `o` could be callable and falsish, such as nonstandard callable
"document.all".

Object `o` could be the `item` method, for which typeof will result
"string" in IE. This would result in isHostMethod returning false.
 
G

Garrett Smith

David said:
I have posted a new primer related to host objects and feature
detection/testing.

http://www.cinsoft.net/host.html

| The isHostObjectProperty function tests if the specified host object
| property references an object that is safe to evaluate.

The term "evaluate" is non-standard terminology. What do you mean?

[snip comments about John Resig]
 
D

David Mark

Garrett said:
Thomas said:
David said:
I have posted a new primer related to host objects and feature
detection/testing.
[...]


ISTM the RegExp is borken:

var reFeaturedMethod = new RegExp('^function|object$', 'i');

It matches case-insensitive either "function" at the begin of input or
"object" at the end, when it should match case-insensitive an input
that is either "function" or "object":

var reFeaturedMethod = new RegExp('^(function|object)$', 'i');

A Literal would be shorter and would stay cached:

/^(?:func|obj)/;

I fail to see how that is the same thing, but the non-capturing bit is a
good idea.

As for caching, I don't see how it makes any difference as I create the
RegExp object once.
Furthermore, AISB,

return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');

becomes more efficient when writing

return !!(t == 'unknown' || (reFeaturedMethod.test(t) && o[m]));

The double negation to cast to boolean is a matter of taste; I do not
think it is necessary, because one possible result is a boolean
already and the other has been proven by this that it can be used in a
type-converting test.
Double negation on a boolean is pointless. However, `o[m]` should not be
a boolean; it should be a function or an object.
Right.


Caveats:
Object `o` could be callable and falsish, such as nonstandard callable
"document.all".

Object `o` could be the `item` method, for which typeof will result
"string" in IE. This would result in isHostMethod returning false.

Yes, I should add both of those stipulations to the docs and this example.
 
D

David Mark

Garrett said:
| The isHostObjectProperty function tests if the specified host object
| property references an object that is safe to evaluate.

The term "evaluate" is non-standard terminology. What do you mean?

Anything along the lines of type conversion, assigning a reference to a
variable, etc. What would you call it?
 
D

David Mark

kangax said:
Is there a reason `isHostObjectProperty` is not called `isHostProperty`
(to be consistent with `isHostMethod`)?

The "Object" goes with "Property", not the "Host" part.
Also, `findProperietaryStyle` doesn't include "Ms" prefix. Why?
(<http://blogs.msdn.com/ie/archive/2008/09/08/microsoft-css-vendor-extensions.aspx>)

Happenstance. It was written before IE8 came out and the sorts of
things I've used it for have had only non-CSS equivalents (e.g. opacity,
transform). I'll add it as they do have a handful of proprietary CSS3
implementations that could be useful to detect (e.g. text-overflow). I
thought I had since added MS for that one in particular, but it may have
been on some other project (I hastily copied the posted implementation
from a My Library add-on).
Finally, it might be worth mentioning that `isEventSupported` could (and
_does_, as any other inference) return false positives; from those I
know about — `window`'s "error" in Chrome (present but "defunct"), and
"contextmenu" in Opera 10.50 (even when corresponding option is off in
settings!).

It is only meant to be used with elements (which I should stipulate of
course). As for "contextmenu", I never considered that a false
positive. The event is supported, but like many things in browsers, the
user has the ability to get in the way. But from your wording, it
sounds as if there is a bug in Opera 10.5 that should be noted (and
reported).

Thanks for the input!
 
T

Thomas 'PointedEars' Lahn

David said:
I fail to see how that is the same thing, but the non-capturing bit is a
good idea.

Not if you want this to be backwards-compatible. The Matrix says:

ES JavaScript JScript V8 JSCore Opera KJS
/(?:…)/ : RegExp 3 1.5 5.5.6330 1.3 525.13 7.02 3.5.9
As for caching, I don't see how it makes any difference as I create the
RegExp object once.

The question is moot anyway since ES5 implementations instantiate a new
object each time they encounter the literal. (This is different in ES3.)
Furthermore, AISB,

return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');

becomes more efficient when writing

return !!(t == 'unknown' || (reFeaturedMethod.test(t) && o[m]));

The double negation to cast to boolean is a matter of taste; I do not
think it is necessary, because one possible result is a boolean
already and the other has been proven by this that it can be used in a
type-converting test.

Double negation on a boolean is pointless.
Yes.
However, `o[m]` should not be a boolean; it should be a function or an
object.

I beg your pardon?

return (t == 'unknown' || (reFeaturedMethod.test(t) && !!o[m]));

But AISB the `!!' does not really save anything as in a boolean context the
return value would be subject to type conversion anyway.

Is there a good reason for document.all(...) instead of document.all[...]?
If not, that fact is largely irrelevant.
Yes, I should add both of those stipulations to the docs and this
example.

That argument only makes sense if _o[m]_ refers to the item() method of
NodeList or HTMLCollection implementations. Then again, is there a good
reason to call o.item(i) instead of accessing o? If not, that fact is
largely irrelevant.


PointedEars
 
D

David Mark

Thomas said:
Not if you want this to be backwards-compatible. The Matrix says:

ES JavaScript JScript V8 JSCore Opera KJS
/(?:…)/ : RegExp 3 1.5 5.5.6330 1.3 525.13 7.02 3.5.9

I wasn't sure about that, which is why I tend to stick with what I know
for sure. Thanks for illuminating that. Verboten for My Library.
The question is moot anyway since ES5 implementations instantiate a new
object each time they encounter the literal. (This is different in ES3.)

Interesting. What possessed them to do that?
Furthermore, AISB,

return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');

becomes more efficient when writing

return !!(t == 'unknown' || (reFeaturedMethod.test(t) && o[m]));

The double negation to cast to boolean is a matter of taste; I do not
think it is necessary, because one possible result is a boolean
already and the other has been proven by this that it can be used in a
type-converting test.
Double negation on a boolean is pointless.
Yes.
However, `o[m]` should not be a boolean; it should be a function or an
object.

I beg your pardon?

o[m] will not normally be a boolean, hence the double negation.

return (t == 'unknown' || (reFeaturedMethod.test(t) && !!o[m]));

But AISB the `!!' does not really save anything as in a boolean context the
return value would be subject to type conversion anyway.

That's true. But I prefer to have the function return booleans only.
Is there a good reason for document.all(...) instead of document.all[...]?
If not, that fact is largely irrelevant.

I missed that that second part was mentioned. I already mentioned about
the sometimes callable objects in the explanation and documentation.
Don't request an opinion from isHostMethod on those.
Yes, I should add both of those stipulations to the docs and this
example.

That argument only makes sense if _o[m]_ refers to the item() method of
NodeList or HTMLCollection implementations. Then again, is there a good
reason to call o.item(i) instead of accessing o? If not, that fact is
largely irrelevant.


Right. It is odd that the one exception to an otherwise golden rule is
something you would/should never need anyway. Still, it's an
interesting caveat and I think I will mention it.
 
G

Garrett Smith

David said:
Garrett said:
Thomas said:
David Mark wrote:

I have posted a new primer related to host objects and feature
detection/testing. [...]

ISTM the RegExp is borken:

var reFeaturedMethod = new RegExp('^function|object$', 'i');

It matches case-insensitive either "function" at the begin of input or
"object" at the end, when it should match case-insensitive an input
that is either "function" or "object":

var reFeaturedMethod = new RegExp('^(function|object)$', 'i');
A Literal would be shorter and would stay cached:

/^(?:func|obj)/;

I fail to see how that is the same thing, but the non-capturing bit is a
good idea.
It is not the same thing.

Either would do the job as well as:
/^(?:function|object)$/;

Being case-insensitive is pointless, though. I'd ditch the 'i' flag
either way.
As for caching, I don't see how it makes any difference as I create the
RegExp object once.

The difference would be when the object is created. Either at runtime
(as with constructor) or during lexical scan for regexp literal.
 
G

Garrett Smith

David said:
Anything along the lines of type conversion, assigning a reference to a
variable, etc. What would you call it?

I like to see the standard terminology to describe the problems.
I mentioned a few of the problems with host objects here:

http://jibbering.com/faq/notes/code-guidelines/#hostObjects

Posted inline, for convenience:
| Host Objects:
|
| * Operators:
| o Do not use delete operator with host object (IE Errors)
| o Do not add any expando properties (unselectable is safe)
| o Host objects that error upon [[Get]] access are often ActiveX
| objects. These include, but are not limited to:
| + Disconnected nodes whose parentNode is not an element
| (node.offsetParent)
| + XMLHttpRequest methods (open, send, etc).
| + filters: elem.filters.alpha, elem.style.filters.alpha, etc.
| + document.styleSheets[99999] - Error from [[Get]] for a
| nonexistent numeric property of a styleSheets collection.
| + link.href for nntp: links in IE.
| + NodeList in Safari 2 - do not attempt access a nonexistent
| property (e.g. document.childNodes.slice).
|
| * Type conversion
| [[ToString]]
| Perform string conversion by starting concatenation with a string
| value. See Newsgroup message explanation.
<URL:
http://groups.google.bg/group/comp.lang.javascript/msg/1528f612e31f09fe >
 
G

Garrett Smith

kangax said:
[...]

Yeah, I should report it to them. The fact that Opera bug tracker is not
open is annoying (I have no idea what's going on with the bugs I filed
in the past).
"Signs point to yes"
(source: magic 8 ball).
 
G

Garrett Smith

David said:
[...]
But AISB the `!!' does not really save anything as in a boolean context the
return value would be subject to type conversion anyway.

That's true. But I prefer to have the function return booleans only.
Having gthe function return boolean provides clear expectations to the
caller. WIth an "is" method, the caller should be able to expect a
boolean value.

This expectation could be clearly defined by a unit test. I might write
it like this:

"testIsHostMethod - contains" : function(){
var actualResult = isHostMethod(document.body.contains);
Assert.isTrue(actualResult);
}

That isHostMethod returning something other than false would end up
failing that test. By always returning boolean value, the expectation is
simpler.
Caveats:
Object `o` could be callable and falsish, such as nonstandard callable
"document.all".
Is there a good reason for document.all(...) instead of document.all[...]?
If not, that fact is largely irrelevant.

I missed that that second part was mentioned. I already mentioned about
the sometimes callable objects in the explanation and documentation.
Don't request an opinion from isHostMethod on those.

I was not suggesting a workaround for the document.all anomaly.

The use of document.all should be abstained from.
Object `o` could be the `item` method, for which typeof will result
"string" in IE. This would result in isHostMethod returning false.
Yes, I should add both of those stipulations to the docs and this
example.
That argument only makes sense if _o[m]_ refers to the item() method of
NodeList or HTMLCollection implementations. Then again, is there a good
reason to call o.item(i) instead of accessing o? If not, that fact is
largely irrelevant.


Right. It is odd that the one exception to an otherwise golden rule is
something you would/should never need anyway. Still, it's an
interesting caveat and I think I will mention it.


I can't think of a good reason for preferring item() over [].

I recall testing Firefox up to 1.5 and [] was faster than item() there.
Browsers nowadays are so fast that that difference (which may not exist
any longer) would hardly matter much.
 
D

David Mark

kangax said:
I understand that :) But what does "Property" clarify there? What's
wrong with having `isHostMethod` and `isHostProperty` — where first one
is for testing anything that's intended to be called (i.e. method), and
latter — for anything that won't be called (i.e. property).

Because it only tests for object properties (i.e. not booleans, strings,
numbers, etc.)
[...]
It is only meant to be used with elements (which I should stipulate of
course). As for "contextmenu", I never considered that a false
positive. The event is supported, but like many things in browsers, the
user has the ability to get in the way. But from your wording, it
sounds as if there is a bug in Opera 10.5 that should be noted (and
reported).

Yeah, I should report it to them. The fact that Opera bug tracker is not
open is annoying (I have no idea what's going on with the bugs I filed
in the past).

Yeah, I hope they get that fixed. ISTM, their preferences dialog
sometimes gets out of whack with the reality of the browser window.
Still, I like it and have taken to using it as my primary browser.
 
D

David Mark

Garrett said:
David said:
Garrett said:
Thomas 'PointedEars' Lahn wrote:
David Mark wrote:

I have posted a new primer related to host objects and feature
detection/testing.
[...]

ISTM the RegExp is borken:

var reFeaturedMethod = new RegExp('^function|object$', 'i');

It matches case-insensitive either "function" at the begin of input or
"object" at the end, when it should match case-insensitive an input
that is either "function" or "object":

var reFeaturedMethod = new RegExp('^(function|object)$', 'i');

A Literal would be shorter and would stay cached:

/^(?:func|obj)/;

I fail to see how that is the same thing, but the non-capturing bit is a
good idea.
It is not the same thing.

Either would do the job as well as:
/^(?:function|object)$/;

I don't want to let anything through that is "func" or "obj". That's a
slippery slope (i.e. why not just test for "fu" and "ob").
Being case-insensitive is pointless, though. I'd ditch the 'i' flag
either way.

I suppose.
The difference would be when the object is created. Either at runtime
(as with constructor) or during lexical scan for regexp literal.

Right, but I didn't understand what was meant by "caching" as it is a
one-shot deal in either case.
 
D

David Mark

Garrett said:
I like to see the standard terminology to describe the problems.

Yes, and that would be what in this case? I mean a single word to
replace evaluate. I realized when I wrote it it wasn't technically
specified, but couldn't come up with a better word.
I mentioned a few of the problems with host objects here:

http://jibbering.com/faq/notes/code-guidelines/#hostObjects

Yes, and speaking of the FAQ:-

http://www.jibbering.com/faq/#onlineResources

....needs section for browser scripting resources (e.g. mine, Kangax'
blog, etc.) And:-

http://www.jibbering.com/faq/faq_notes/contributors.html

....needs my name added. At the very least, the confirm issue I fixed:-

http://www.jibbering.com/faq/#changeBrowserDialog
Posted inline, for convenience:
| Host Objects:
|
| * Operators:
| o Do not use delete operator with host object (IE Errors)

Sound, but I would ditch the parenthetical. Could happen to any browser.
| o Do not add any expando properties (unselectable is safe)

What is this one's aside about?
| o Host objects that error upon [[Get]] access are often ActiveX
| objects. These include, but are not limited to:

Host object _properties_ that throw errors on [[Get]] (a term that is
too subterranean for my primers) often indicate that the containing
object is an ActiveX implementation. All such method properties do it.
| + Disconnected nodes whose parentNode is not an element
| (node.offsetParent)

In some cases, all properties of element nodes go AWOL ("unknown" typeof
results). IIRC, that happens when they are orphaned by an innerHTML
replacement.
| + XMLHttpRequest methods (open, send, etc).

And its ActiveX equivalents.
| + filters: elem.filters.alpha, elem.style.filters.alpha, etc.

The filters object is implemented with ActiveX, so its properties are
suspect.
| + document.styleSheets[99999] - Error from [[Get]] for a
| nonexistent numeric property of a styleSheets collection.

That one may not be due to ActiveX, but just an allowable exception for
an out of bounds request.
| + link.href for nntp: links in IE.

Yes, the Stockton href incident. That one was truly unexpected (and
likely a bug) as how else are you to get the href value. (!)
| + NodeList in Safari 2 - do not attempt access a nonexistent
| property (e.g. document.childNodes.slice).

That's an odd one. Likely also a bug.
|
| * Type conversion
| [[ToString]]
| Perform string conversion by starting concatenation with a string
| value. See Newsgroup message explanation.
<URL:
http://groups.google.bg/group/comp.lang.javascript/msg/1528f612e31f09fe >

I don't see how the explanation relates to host objects, which don't
have to follow the specs at all.
 
D

David Mark

Garrett said:
David said:
Thomas said:
David Mark wrote:

Garrett Smith wrote:
Thomas 'PointedEars' Lahn wrote:
var reFeaturedMethod = new RegExp('^(function|object)$', 'i
[...]
But AISB the `!!' does not really save anything as in a boolean
context the return value would be subject to type conversion anyway.

That's true. But I prefer to have the function return booleans only.
Having gthe function return boolean provides clear expectations to the
caller. WIth an "is" method, the caller should be able to expect a
boolean value.

This expectation could be clearly defined by a unit test. I might write
it like this:

"testIsHostMethod - contains" : function(){
var actualResult = isHostMethod(document.body.contains);
Assert.isTrue(actualResult);
}

isHostMethod(document.body, 'contains')

But I don't see that as a good unit test for this method as it will fail
if that host method is missing. I would prefer to simply test that the
result is a boolean.
That isHostMethod returning something other than false would end up
failing that test. By always returning boolean value, the expectation is
simpler.

Perhaps I am reading your test wrong. Did you mean something like
isBoolean (and returns something other than true/false?)
Caveats:
Object `o` could be callable and falsish, such as nonstandard callable
"document.all".
Is there a good reason for document.all(...) instead of
document.all[...]?
If not, that fact is largely irrelevant.

I missed that that second part was mentioned. I already mentioned about
the sometimes callable objects in the explanation and documentation.
Don't request an opinion from isHostMethod on those.

I was not suggesting a workaround for the document.all anomaly.

The use of document.all should be abstained from.
Absolutely.
Object `o` could be the `item` method, for which typeof will result
"string" in IE. This would result in isHostMethod returning false.
Yes, I should add both of those stipulations to the docs and this
example.
That argument only makes sense if _o[m]_ refers to the item() method
of NodeList or HTMLCollection implementations. Then again, is there
a good reason to call o.item(i) instead of accessing o? If not,
that fact is largely irrelevant.


Right. It is odd that the one exception to an otherwise golden rule is
something you would/should never need anyway. Still, it's an
interesting caveat and I think I will mention it.


I can't think of a good reason for preferring item() over [].


Me neither. I've never used it.
I recall testing Firefox up to 1.5 and [] was faster than item() there.
Browsers nowadays are so fast that that difference (which may not exist
any longer) would hardly matter much.

Less operations would seem to indicate it would be faster, but you can
never be 100% sure with such a small variation (and, as noted, it's not
likely to be a significant difference anyway).
 
G

Garrett Smith

David said:
Garrett said:
David said:
Thomas 'PointedEars' Lahn wrote:
David Mark wrote:

Garrett Smith wrote:
Thomas 'PointedEars' Lahn wrote:
var reFeaturedMethod = new RegExp('^(function|object)$', 'i [...]

But AISB the `!!' does not really save anything as in a boolean
context the return value would be subject to type conversion anyway.
That's true. But I prefer to have the function return booleans only.
Having gthe function return boolean provides clear expectations to the
caller. WIth an "is" method, the caller should be able to expect a
boolean value.

This expectation could be clearly defined by a unit test. I might write
it like this:

"testIsHostMethod - contains" : function(){
var actualResult = isHostMethod(document.body.contains);
Assert.isTrue(actualResult);
}

isHostMethod(document.body, 'contains')

Right.

But I don't see that as a good unit test for this method as it will fail
if that host method is missing. I would prefer to simply test that the
result is a boolean.
That isHostMethod returning something other than false would end up
failing that test. By always returning boolean value, the expectation is
simpler.

Perhaps I am reading your test wrong. Did you mean something like
isBoolean (and returns something other than true/false?)

No, that makes sense, but it is not what I meant.

I was going for testing under known conditions, that the method does
what is expected it will do.

A "contains" method will be absent in some implementations, so no good
as test.

A valid test might be to set up a case where the result is known or
assumed to be true or false. For example, if the test case assumes that
the environment has a `document.getElementById` that is potentially
callable and that `document.title` would never be callable:

testIsHostMethodWithDomCoreMethod : function() {
Assert.isTrue(isHostMethod(document, "getElementById"));
}

testIsHostMethodWithNonCallableObject : function() {
Assert.isFalse(isHostMethod(document, "title"));
}

The expected outcome could also be dynamic. For example:

"testIsHostMethod - element.contains()" : function() {
// First determine if calling something either works or doesn't.
var expectedCallability = (function(){
try {
document.body.contains(document.body);
return true;
} catch(ex) {
return false;
}
})();

Assert.areSame(expectedCallability ,
isHostMethod(document.body, "contains");
}

The only problem with this is that testing a property such as
`document.forms` or `document.images` will be true, but not always callable.

[...]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,522
Members
44,995
Latest member
PinupduzSap

Latest Threads

Top