From: VK on
On May 11, 7:52 pm, Matt Kruse <m...(a)thekrusefamily.com> wrote:
> > Does anyone here use a general convenience method for deep property
> > access that will not throw an error if any property along the chain is
> > undefined?

And what is wrong with throwing errors if they are properly caught and
handled?

try {
prop = window.document.foo.bar;
}
catch (e) {
/* NOP */
}

I assume NN4/IE4 issue is not an issue anymore.

It can be also used "Guard AND" as Crockford calls it. It is much
longer but fully reliable as well:

prop = (window && window.document &&
window.document.foo && window.document.foo.bar);

I am using the first, you may use either one.

There are also situations when a property exists but named differently
in different UAs, like "innerText" (IE) and "textContent" (many
others). Here bracket property access comes handy, i.e.:

var text = (/*@cc_on true || @*/ false) ? 'innerText' : 'textContent';
// ...
var t = document.getElementById('elm')[text];

A very particular case is document.all collection in Firefox. It is
always undefined, but in quirk (backCompat) mode it does exists and
can be used just like in IE, so

if (document.all) { // always false
}

but

if (document.compatMode == 'BackCompat') {
var foo = document.all.SomeElement;
// will be fine
}

Obviously in this case the "guard AND" will not do the trick, try-
catch is always reliable. The issue has a very little practical
importance, just to mention for the completeness.
From: Stefan Weiss on
On 12/05/10 21:57, Garrett Smith wrote:
> Stefan Weiss wrote:
>> On 11/05/10 15:44, Matt Kruse wrote:
>>> Does anyone here use a general convenience method for deep property
>
> [...]
>
>> This may be obvious, but did you consider simply catching and ignoring
>> the ReferenceError?
>>
>> try { var color = body.firstChild.style.backgroundColor; } catch(e) {}
>>
>> Maybe it doesn't look as nice as your deep() call, but at least there's
>> no custom syntax involved, and you can use arbitrary property names,
>> including all the counter examples mentioned by Thomas, and even
>> expressions like childNodes[i+1].
>
> Starting a statement that way isn't "not as nice" it is a failure of the
> code to express its intent. The try statement misleads the reader. Why
> is it there? Can anyone know?

Anyone who knows what the code they're reading is supposed to do should
have a pretty good idea. I agree that it's not pretty, but it becomes
more obvious if you write it like this:

try {
var color = body.firstChild.style.backgroundColor;
} catch (e) {
color = someDefaultValue;
}

I don't think I'd need any additional explanations when I read this,
except in unusual situations, and those should always be commented.
Anyway, you can always write

if (body
&& body.firstChild
&& body.firstChild.style
&& body.firstChild.style.backgroundColor)
{
var color = body.firstChild.style.backgroundColor;
} else {
color = someDefaultValue;
}

if you think that's more readable. To each their own.

> Promoting the idea of try-catch for everything, as Google does, putting
> FunctionDeclaration where only statement may appear is the next obvious
> mistake, as Google does.

I'm not Google.

> Someone may also mention that using try/catch is inefficient, and it is,
> but that is the least of the problems.

Especially when you're afraid to use it because you think it *might* be
slower than a function like $prop, but don't bother testing it first.

In Firefox 3.0, try/catch is 3-4 times faster than $prop, in IE 6 and IE
7 it's about 7 times faster, it's about the same speed in Opera 9.64,
and over 10 times faster in Opera 10.53. I didn't bother with the other
browsers because the notion that try/catch is always inefficient
compared to the $prop function is obviously not correct.


____________________________________

// using Matt's tests

var json = {
'a':'1'
,'b': ['x','y','z']
,'c': {
'array':['1','2','3']
,'property':'prop!'
}
};

JSLitmus.test('prop', function (count) {
while (count--) {
var a = $prop(json, "a");
var b = $prop(json, "b[1]");
var c = $prop(json, "c.array[2]");
var d = $prop(json, "d.e.f.g");
//check(a, b, c, d);
}
});

JSLitmus.test('try', function (count) {
while (count--) {
try { var a = json.a; } catch (e) {}
try { var b = json.b[1]; } catch (e) {}
try { var c = json.c.array[2]; } catch (e) {}
try { var d = json.d.e.f.g; } catch (e) {}
//check(a, b, c, d);
}
});

function check (a, b, c, d) {
if (a !== "1" || b !== "y" || c !== "3" || d !== undefined) {
throw new Error("whoops");
}
}


--
stefan
From: Thomas 'PointedEars' Lahn on
Stefan Weiss wrote:

> On 12/05/10 21:57, Garrett Smith wrote:
>> Stefan Weiss wrote:
>>> On 11/05/10 15:44, Matt Kruse wrote:
>>>> Does anyone here use a general convenience method for deep property
>> [...]
>>> This may be obvious, but did you consider simply catching and ignoring
>>> the ReferenceError?
>>>
>>> try { var color = body.firstChild.style.backgroundColor; } catch(e) {}
>>>
>>> Maybe it doesn't look as nice as your deep() call, but at least there's
>>> no custom syntax involved, and you can use arbitrary property names,
>>> including all the counter examples mentioned by Thomas, and even
>>> expressions like childNodes[i+1].
>>
>> Starting a statement that way isn't "not as nice" it is a failure of the
>> code to express its intent. The try statement misleads the reader. Why
>> is it there? Can anyone know?
>
> Anyone who knows what the code they're reading is supposed to do should
> have a pretty good idea. I agree that it's not pretty, but it becomes
> more obvious if you write it like this:
>
> try {
> var color = body.firstChild.style.backgroundColor;
> } catch (e) {
> color = someDefaultValue;
> }
>
> I don't think I'd need any additional explanations when I read this,
> except in unusual situations, and those should always be commented.

That would imply that you would need to use try-catch block for each single
property access. And you could still not know for sure what it was that
threw the exception. You really don't want to do that.

> Anyway, you can always write
>
> if (body
> && body.firstChild
> && body.firstChild.style
> && body.firstChild.style.backgroundColor)
> {
> var color = body.firstChild.style.backgroundColor;
> } else {
> color = someDefaultValue;
> }
>
> if you think that's more readable. To each their own.

You miss the point. The purpose of a property-accessor function is to avoid
those maintenance-hardening Guard-ANDs and to make code more readable:

if (isFeature(body, "firstChild", "style", "backgroundColor"))
{
var color = body.firstChild.style.backgroundColor;
} else {
color = someDefaultValue;
}

or even

var color = getProperty(body, "firstChild", "style", "backgroundColor")
|| someDefaultValue;

>> Promoting the idea of try-catch for everything, as Google does, putting
>> FunctionDeclaration where only statement may appear is the next obvious
>> mistake, as Google does.
>
> I'm not Google.

But you seem to think like them.

>> Someone may also mention that using try/catch is inefficient, and it is,
>> but that is the least of the problems.
>
> Especially when you're afraid to use it because you think it *might* be
> slower than a function like $prop, but don't bother testing it first.
>
> In Firefox 3.0, try/catch is 3-4 times faster than $prop,

Depends on what $prop() is. As posted, it is unnecessarily inefficient.

> JSLitmus.test('prop', function (count) {

Don't get me started on that.


PointedEars
--
realism: HTML 4.01 Strict
evangelism: XHTML 1.0 Strict
madness: XHTML 1.1 as application/xhtml+xml
-- Bjoern Hoehrmann
From: Stefan Weiss on
On 12/05/10 23:03, Thomas 'PointedEars' Lahn wrote:
> Stefan Weiss wrote:
>> try {
>> var color = body.firstChild.style.backgroundColor;
>> } catch (e) {
>> color = someDefaultValue;
>> }
>>
>> I don't think I'd need any additional explanations when I read this,
>> except in unusual situations, and those should always be commented.
>
> That would imply that you would need to use try-catch block for each single
> property access.

That's correct.

> And you could still not know for sure what it was that
> threw the exception. You really don't want to do that.

I might, if I'm not interested in the details of any exceptions this
might throw. I will get my property value or my default value; that's
what counts. YMMV.

Anyway, I don't use try/catch here myself at all, I only suggested it as
a possible alternative to deep() / $prop(). The cases where I want to
access some deeply nested property whose parents may not exists just
don't come up very often. I usually know what's there to read, because
I'm usually involved in creating the HTML/XML/JSON/etc tree structure.
In the rare cases when I'm not, I use the guard-AND form mentioned below.

> You miss the point. The purpose of a property-accessor function is to avoid
> those maintenance-hardening Guard-ANDs and to make code more readable:
>
> if (isFeature(body, "firstChild", "style", "backgroundColor"))
> {
> var color = body.firstChild.style.backgroundColor;
> } else {
> color = someDefaultValue;
> }
>
> or even
>
> var color = getProperty(body, "firstChild", "style", "backgroundColor")
> || someDefaultValue;

Looks good to me. I definitively like it a lot better than parsing a
"string.of.property.names". As for readability... your version requires
the reader to know what two vaguely named functions do, whereas
try/catch only uses standard JS features that should be familiar to
everybody.

If this works for you, great. I just don't need it often enough to add
functions like deep() or getProperty().

>> I'm not Google.
>
> But you seem to think like them.

You are wrong about that. Please don't make assumptions about how I
think. If you absolutely have to, please keep them to yourself.

>> In Firefox 3.0, try/catch is 3-4 times faster than $prop,
>
> Depends on what $prop() is.

The definition of $prop() was posted in this thread.

> As posted, it is unnecessarily inefficient.

True. An array- or arguments-based approach will be faster as well as safer.

>> JSLitmus.test('prop', function (count) {
>
> Don't get me started on that.

It's just a litmus test, not an exact measurement. It's sufficient to
get a general idea. If you doubt the results, you can easily verify them
without JSLitmus. But yeah, I don't really want to start about JSLitmus
either; it's been discussed before.


--
stefan
From: Dr J R Stockton on
In comp.lang.javascript message <083890c5-1c81-48db-ac47-adced2d307e8(a)b2
1g2000vbh.googlegroups.com>, Tue, 11 May 2010 06:44:35, Matt Kruse
<matt(a)thekrusefamily.com> posted:

>Does anyone here use a general convenience method for deep property
>access that will not throw an error if any property along the chain is
>undefined?
>
>For example:
>
>deep(document, "body.firstChild.style.backgroundColor");


Something on the general lines of this might resemble the answer to what
seems to be your question ; a little rearrangement could eliminate the
expectation that the first part exists. UNTESTED.

function deep(T, A) { var B = A.split("."), J = 0, U
while (J < B.length) if ( ( T = T[ B[J++] ] ) == U ) break
return T }

--
(c) John Stockton, near London. *@merlyn.demon.co.uk/?.?.Stockton(a)physics.org
Web <URL:http://www.merlyn.demon.co.uk/> - FAQish topics, acronyms, & links.
Correct <= 4-line sig. separator as above, a line precisely "-- " (RFC5536/7)
Do not Mail News to me. Before a reply, quote with ">" or "> " (RFC5536/7)