Re: When is a function not a function?
- From: "Richard Cornford" <Richard@xxxxxxxxxxxxxxxxxxx>
- Date: Sun, 14 Oct 2007 16:18:47 +0100
<dhtmlkitchen@xxxxxxxxx> wrote:
On Oct 13, 7:01 pm, Richard Cornford wrote:<dhtmlkitc...@xxxxxxxxx> wrote:On Sep 26, 1:29 am, Richard Cornford wrote:<snip>Then I came across this.
(!!fn && typeof fn != "string" && !fn.nodeName &&
fn.constructor != Array && /function/i.test( fn + "" ))
I'm sure it is wrong
That would depend on what you considered correct. It looks
like the subject of the test is expected to be any of a
javascript function (unwisely tested), a siring primitive,
a DOM element and Array or a value with falseness. Getting
into a position where it is necessary to make such a test
betrays very poor code design.
> I think you meant "portrays" not "betrays". Am I right?
No, "betrays" says exactly what I intended to say.
That analysis would be right if they'd used ||, but they
used &&.
No, that analysis is correct because it uses &&.
I provided analysis of the problem on my site.
That is not what I would call it. It is a pity that you choose
to publish such things without first having them checked by
someone who knows this subject.
There you go again.
And there you go again. Instead of being interested in why I think 50% of what you wrote is somewhere between uninformed and nonsense you would rather disregard the comment and leave it as it is.
<snip>
The subject is expected NOT to be any of those things.(!!fn )....................falses are out.
A pointless test if the subject is not expected to potentially
be a value that has falseness.
(typeof fn != "string")....string values are out
A pointless test if the subject is not expected to potentially
be a string primitive.
(!fn.nodeName).............things with a truthy nodeName are out.
A pointless test if the subject is not expected to potentially
be an object with a true nodeName property. An expected
characteristic of DOM elements.
(fn.constructor != Array)..Arrays in same frame are out.
A pointless test if the subject is not expected to potentially
be an Array.
(/function/i.test( fn + "" ).anything else left can get
through, it just has to say "function".
So in what sense is my "analysis" not correct? "It looks like the
subject of the test is expected to be any of a javascript function
(unwisely tested), a siring primitive, a DOM element and Array or a
value with falseness".
If the subject (the value that is subject to the tests) were not expected to be a string what would be the point of seeing how the results of - typeof - compared with 'string'? If it could not be a string then the outcome would be known at the point of writing the code and no runtime test would be required. The same goes for every other test.
The point of each test is to exclude these things.
If the subject of the test is not expected to potentially be the things that are excluded there is no point making any effort to exclude them.
Just maybe, there's
some type of node that says it's a "function" to typeof.
There is nothing to say that an object implementing the Node interface should not be a function object.
It's actually likely in Safari; Safari thinks document.images
is a function, which it is Not!
Fist you must state what it is that defined 'a function'. One completely rational and justifiable definition of 'a function' in ECMAScritp terms could be "an object that can be called" (i.e. subject to the 'call operator' without that act of itself directly resulting in an exception being thrown (later exceptions, such as winging about the types or number of arguments are not related to the callability of the object)). Under that definition Safari's - document.images - is a function, as it can be called.
Now I should clear up a bit of confusion you posted on the
webkit bug:
Take an object that is an instance of an NodeList.
NodeList is an interface not a 'class'. Which is a good thing, as ECMAscript/javascript has no notion of 'classes'. But even in class-based languages like Java an object implementing an interface is not necessarily an object of any particular (or any single) class.
Javascript is a very flexible language and it loves interfaces. You can take an object, any object, and retro-fit an interface to it at runtime. And you can add the interface to any number of objects with very diverse origins to the extent that they have nothing else in common that could be regarded as asserting their 'type'.
The result is that any notion of "an instance of an NodeList" has very little meaning in javascript. It may or may not reflect an internal implementation detail, but beyond that it is no more than a perception in the mind of a programmer.
That object's constructor implements NodeList.
Absolutely nothing, anywhere, makes any assertions about constructors in relation to object that implement the NodeList interface.
The object itself is an object.
An object implementing the NodeList interface must be an object, but is not required to be anything more. And it can be expected to be a host object, so almost nothing else can be asserted about it with authority.
The fact that it is callable is a bug,
No. Object can be callable. At least the things that are callable in javascript are all objects.
copied from MSIE, who made document.all, etc. a function,
e.g. document.all(0). Mozilla copied this.
And copied by nearly every other browser since, with a significant proportion of them also returning 'function' from - typeof - operations on those objects.
It is retarded, IMO.
It is certainly unnecessary and unhelpful, but it is (and has for some considerable time been) a reality.
javascript:alert(Function.prototype.call.call(
document.links,document, 1))
Not really useful. OR good design.
An instance of a NodeList is NOT the object that
implements NodeList.
An object that implements the NodeList interface is precisely what the W3C DOM specifications define it as. Making any assertion beyond that would be technically groundless.
The object that implement's NodeList is the instance's
constructor.
Can you find any authorities statement supporting that assertion? Neither of the applicable documents (Core DOM and its ECMAScript bindings and ECMA 262) are that big so it should not take too long to look.
What objects are those?It's not safe, but it's indicative of the problem of
typechecking in js.
Dojo has similar stuff.
The dojo version of isFunction is nearly as bad as it will
also return true for non-callable objects.
Objects that inherit from functions.
Only ones that incorrectly report "function"
for typeof, I hope.
Have you identifier any objects that do "incorrectly" report 'function'? We have seen that the objects that are concerning you are callable (which would be a fine justification for reporting 'function') and that they are host objects (so they may report anything at all). Where does your definition of "incorrectly" come from?
What I prefer about the dojo function
This is the dojo function:-
| if(dojo.isBrowser && dojo.isSafari){
| // only slow this down w/ gratuitious casting in Safari since
| // it's what's b0rken
| dojo.isFunction = function(/*anything*/ it){
| if((typeof(it) == "function") && (it == "[object NodeList]")){
| return false;
| }
| return (typeof it == "function" || it instanceof Function);
| }
| }else{
| dojo.isFunction = function(/*anything*/ it){
| return (typeof it == "function" || it instanceof Function);
| }
| }
is at least it is less inclusive.
It filters out with the typeof operator.
document.links instanceof Function; // false in Safari.
But it will include any non-function object that has a function on its prototype chain, so the objects that are 'isFunction' are not necessarily even executable. It makes you wonder what it is they think they are testing for.
<snip>
That's what I'm saying. If jQuery's isFunction gets a falsish value,You're confused by thinking ||. The function uses &&
No I am not. To get a true result with the logical AND operation
test every individual sub-expression needs to evaluate to true.
a string value, et c, that value is excluded. It's "none of", not
"any of". You're wrong.
No I am right. If the subject of the test (the value that is tested) were not expected to be any of those possibilities then there would be no point in testing them with the intention of excluding them.
http://www.m-w.com/dictionary/betrayAnyway, typechecking is an issue
Not really. Once you get used to properly employing a loosely
typed language it rapidly becomes a non-issue, and such testing
is relegated to the feature testing role alone. Hence my assertion
that the test betrays poor design. Before someone has the
experience to cope with loose typing they will be struggling to
identify the types of things from a position of being totally in
the dark. This results in such testing functions being passed
almost anything at all, and so having to jump through hoops
applying tests that really should never be necessary.
I'm pretty sure you meant "betrays good design," as that's the
gist of what you're saying.
It is in the nature of bad design that it attempts to hide. Here the bad design has been exposed by the test code. The bad design has been betrayed by the code and so its attempt to hide from observation has been unsuccessful. Thus the test code betrays the bad design.
<snip>
When I use others' code, I want it to fail right away if I use it
wrong. Now it might have been my fault for passing in an undefined,
but hey, I'm human and I lose my keys, I've put *** in the washer
that shouldn't go there, (phone, et c).
When others use my code it should fail fast and report the error
properly.
Now if some library had a function that takes a callback, that would
be when you want to know "is the callback a function?"
It would be better to have some more precise definition of what you meant by 'a function' here. Do you just mean something that can be called, or do you really want to discriminate between programmer defined functions and everything else? Pinning down precisely what you need to know is a necessary pre-requisite of designing an effective test that will tell you that.
function doStuff( errorHandler ) {
}
When will errorHandler be invoked? Maybe never, right?
That would depend on your testing. Including an error handler and then not testing whether it handled the errors correctly would seem reckless to me.
Or maybe it will be invoked some time after deployment, maybe
even after I'm gone from the job. Wow, that would suck.
So you would write code for a job and then walk away without testing that it worked as designed?
Wouldn't it make sense to check the errorHandler 'thing' and
make sure it's actually a function first?
function doStuff( errorHandler ) {
if(typeof errorHandler != "function") "
var err = new Error("hey stoopid...");
log(err);
throw err;
...
}
Not as much sense as deliberately provoking the error in testing and so knowing that it did work.
<snip>He's interested in these things, so improvement is
inevitable.
That may be likely, but not inevitable. It appears that becoming
satisfied with what you have done can seriously inhibit an
ability to improve.
Richard.
.
- Follow-Ups:
- Re: When is a function not a function?
- From: David Mark
- Re: When is a function not a function?
- References:
- Re: When is a function not a function?
- From: dhtmlkitchen@xxxxxxxxx
- Re: When is a function not a function?
- From: Richard Cornford
- Re: When is a function not a function?
- From: dhtmlkitchen@xxxxxxxxx
- Re: When is a function not a function?
- Prev by Date: Re: When is a function not a function?
- Next by Date: Re: When is a function not a function?
- Previous by thread: Re: When is a function not a function?
- Next by thread: Re: When is a function not a function?
- Index(es):