Error-free deep property access?

220 views
Skip to first unread message

Matt Kruse

unread,
May 11, 2010, 9:44:35 AM5/11/10
to
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");
or
deep("myElementId.childNodes[3].id");
or
deep( myJsonObject,
"locations.ca.sandiego.directory.people.smith.john.phone");

This would return undefined if any element along the chain is not
there, or index is out of bounds, etc. If the chain gets to the end,
it would return the last-evaluated value. This would be convenient for
accessing deep properties of objects that may or may not exist, and
not having to manually check the chain in your code.

I'm curious to know if anyone uses such an approach, or the cons of
doing so.

Matt Kruse

Scott Sauyet

unread,
May 11, 2010, 10:12:59 AM5/11/10
to
On May 11, 9:44 am, Matt Kruse <m...@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? [ ... ]

>
> I'm curious to know if anyone uses such an approach, or the cons of
> doing so.

I used such a technique only once. It was easy enough to write,
performed well enough for my uses, and did simplify some code. But as
new developers came aboard the project, it was one more thing that
they hadn't seen before, and it rarely turned out to be useful enough
to justify even the short learning curve.

But then again I don't think I've ever written something as deep as
your third example.

-- Scott

Thomas 'PointedEars' Lahn

unread,
May 11, 2010, 11:29:33 AM5/11/10
to
Matt Kruse 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?
>
> For example:
>
> deep(document, "body.firstChild.style.backgroundColor");
> or
> deep("myElementId.childNodes[3].id");
> or
> deep( myJsonObject,
> "locations.ca.sandiego.directory.people.smith.john.phone");

getFeature() and isMethod() in JSX:object.js do a similar thing. They could
be rewritten to support your requirements using dotsToBrackets() in
JSX:types.js. Note that I had opted for several arguments instead because
property names may contain `.', `[', or `]'.

PointedEars
--
Danny Goodman's books are out of date and teach practices that are
positively harmful for cross-browser scripting.
-- Richard Cornford, cljs, <cife6q$253$1$8300...@news.demon.co.uk> (2004)

Matt Kruse

unread,
May 11, 2010, 11:52:49 AM5/11/10
to
On May 11, 8:44 am, Matt Kruse <m...@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?

Well, here's my stab at it:

/*
Error-Free Deep Property Access!

Returns: Property value, or undefined if any part of the property
chain is undefined

Usage:
$prop( object, 'property_name' )
$prop( 'element_id.property_name' )
$prop( 'element_id.prop1.prop2.prop3' )
$prop( object, 'array_property[0]' )
$prop( object, 'method(arg).property_name.array_property[0]
[1].prop' )
$prop( window, 'document.getElementsByTagName(div)
[0].childNodes[1].style.color' )

Special Usage:
$prop() returns the object last evaluated!
if ($prop("id.style.color")) {
alert( $prop() );
}

JSON Example:

var json = {
'a':'1'
,'b': ['x','y','z']
,'c': {
'array':['1','2','3']
,'property':'prop!'
}
}
$prop(json,'a') ==> 1
$prop(json,'b[1]') ==> y
$prop(json,'c.array[2]') ==> 3
$prop(json,'d.e.f.g') ==> undefined

*/
var $prop = (function() {
var last_match;
return function(a,b) {
// Calls to $property() return the last match
if (typeof a=="undefined") { return last_match; }

var context, props = null, p;
// If first arg is not a string, assume it's an object to
// start from
if (typeof a!="string") {
context = a;
props = b.split(".");
}
// Otherwise it's just a string where the first part is an
// element ID
else {
props = a.split(".");
context = document.getElementById(props.shift());
if (!context) { return; }
}
while (context && props.length>0) {
if (context==null) { return; }
p = props.shift();
// If there is an array index [i] at the end, only
// process the first part and stick the second part
// back on the beginning
if ( p.match(/(.+?)(\[\d+\].*)/) ) {
p = RegExp.$1;
props.unshift(RegExp.$2);
}
// if the first part itself is an array index [i]
// then process it
if ( p.match(/^\[(\d+)\]$/) ) {
if (!context || !context.length) { return; }
context = context[RegExp.$1];
}
// If it's a function(arg) call
else if ( p.match(/(.*)\((.*?)\)/) ) {
context = context[RegExp.$1](RegExp.$2);
}
// Else it's a regular property
else {
context = context[p];
}
if (typeof context=="undefined") { return; }
}
last_match = context;
return context;
}
})();

Matt Kruse

Scott Sauyet

unread,
May 11, 2010, 1:19:12 PM5/11/10
to
Matt Kruse wrote:
> On May 11, 8:44 am, Matt Kruse <m...@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?
>
> Well, here's my stab at it: [ ... ]

So this won't allow, for instance,

$props("myObj.prop1.prop2[current.value]")

Is that right? It needs to have only a single root at the first
token. That will cover a lot of possibilities, but the more general
case is interesting too.

--
Scott

Matt Kruse

unread,
May 11, 2010, 1:31:26 PM5/11/10
to
On May 11, 12:19 pm, Scott Sauyet <scott.sau...@gmail.com> wrote:
> So this won't allow, for instance,
>     $props("myObj.prop1.prop2[current.value]")
> Is that right?  It needs to have only a single root at the first
> token.  That will cover a lot of possibilities, but the more general
> case is interesting too.

True. But since you're passing in a string, couldn't you just do:

$prop("myObj.prop1.prop2."+current.value)
?

Matt

Thomas 'PointedEars' Lahn

unread,
May 11, 2010, 2:03:39 PM5/11/10
to
Matt Kruse wrote:

Generally, no. Think about it. See also the caveats that I have mentioned.


PointedEars
--
Anyone who slaps a 'this page is best viewed with Browser X' label on
a Web page appears to be yearning for the bad old days, before the Web,
when you had very little chance of reading a document written on another
computer, another word processor, or another network. -- Tim Berners-Lee

Scott Sauyet

unread,
May 11, 2010, 2:06:31 PM5/11/10
to

Sure, unless current is undefined, which I think is the original
problem we're trying to solve. :-)

And of course we could want

$props("myObj.prop1.prop2[current.deeply.nested.value]")

as well.

--
Scott

Thomas 'PointedEars' Lahn

unread,
May 11, 2010, 2:39:53 PM5/11/10
to
Scott Sauyet wrote:

> Matt Kruse wrote:
>> On May 11, 12:19 pm, Scott Sauyet <scott.sau...@gmail.com> wrote:
>>> So this won't allow, for instance,
>>> $props("myObj.prop1.prop2[current.value]")
>>> Is that right? It needs to have only a single root at the first
>>> token. That will cover a lot of possibilities, but the more general
>>> case is interesting too.
>>
>> True. But since you're passing in a string, couldn't you just do:
>>
>> $prop("myObj.prop1.prop2."+current.value)
>> ?
>

> Sure, unless current is undefined, [...]

Wrong. Think about what would happen with

current.value = 42;

or

current.value = "foo.bar";

or

current.value = "foo['bar']";

Thomas 'PointedEars' Lahn

unread,
May 11, 2010, 2:45:43 PM5/11/10
to
Matt Kruse wrote:

> if ( p.match(/(.+?)(\[\d+\].*)/) ) {
> p = RegExp.$1;
> props.unshift(RegExp.$2);

Other flaws of this approach that I have already mentioned aside, the `$n'
properties of `RegExp' are deprecated as of JavaScript 1.5 at least. Use
the return values of String.prototype.match() and RegExp.prototype.exec(),
respectively, instead.

<https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Deprecated_Features#RegExp_Properties>

Asen Bozhilov

unread,
May 11, 2010, 3:45:13 PM5/11/10
to
Matt Kruse 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?
>
> For example:
>
> deep(document, "body.firstChild.style.backgroundColor");
> or
> deep("myElementId.childNodes[3].id");

Square bracket notation allow to used returned result by expression,
and that result is not bound as Identifiers rules defined by ECMA-262.
With your strategy you would have problems. For example:

var obj = {};
obj['.prop.'] = true;
obj['[prop]'] = true;

print(obj['.prop.']);
print(obj['[prop]'])

Your strategy is too complex and implementation will be terrible any
time. For example you can use evil for your purposes:

eval(properties);

Of course that is not solve the design problems. I will prefer to use
array which contain each property name. For example:

deep(context, ['property1', 'property2', 'propertyN']);


Matt Kruse

unread,
May 11, 2010, 4:59:48 PM5/11/10
to
On May 11, 1:06 pm, Scott Sauyet <scott.sau...@gmail.com> wrote:
> > True. But since you're passing in a string, couldn't you just do:
> > $prop("myObj.prop1.prop2."+current.value)
> > ?
> Sure, unless current is undefined, which I think is the original
> problem we're trying to solve.  :-)

I think it's good to solve the most general case that is reasonable
and useful, but not EVERY general case.

> And of course we could want
>     $props("myObj.prop1.prop2[current.deeply.nested.value]")
> as well.

Sure, you COULD do that, but I don't imagine I ever WOULD. So solving
that case holds little value for me. But using my function, I suppose
you could do:

if ( $prop("current.deeply.nested.value") ) {
$prop("myObj.prop1.prop2."+$prop())
}

Matt Kruse

Matt Kruse

unread,
May 11, 2010, 5:03:28 PM5/11/10
to
On May 11, 2:45 pm, Asen Bozhilov <asen.bozhi...@gmail.com> wrote:
> Square bracket notation allow to used returned result by expression,
> and that result is not bound as Identifiers rules defined by ECMA-262.
> With your strategy you would have problems. For example:
> var obj = {};
> obj['.prop.'] = true;
> obj['[prop]'] = true;
>
> print(obj['.prop.']);
> print(obj['[prop]'])
>
> Your strategy is too complex

But why would you EVER do that? I'm not building this for idiots!

> and implementation will be terrible any
> time.

I don't know, my implementation is pretty straight-forward and works
well.

> For example you can use evil for your purposes:
> eval(properties);

Which will still throw errors.

> I will prefer to use
> array which contain each property name. For example:
> deep(context, ['property1', 'property2', 'propertyN']);

How is that any better than joining the array with "." and passing a
single string?!

Matt Kruse

Garrett Smith

unread,
May 11, 2010, 5:07:38 PM5/11/10
to
Asen Bozhilov wrote:
> Matt Kruse wrote:
>

[...]

> eval(properties);
>
> Of course that is not solve the design problems. I will prefer to use
> array which contain each property name. For example:
>
> deep(context, ['property1', 'property2', 'propertyN']);
>

Does deep check own or prototype?
--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/

Thomas 'PointedEars' Lahn

unread,
May 11, 2010, 5:10:20 PM5/11/10
to
Matt Kruse wrote:

> Asen Bozhilov wrote:
>> Square bracket notation allow to used returned result by expression,
>> and that result is not bound as Identifiers rules defined by ECMA-262.
>> With your strategy you would have problems. For example:
>> var obj = {};
>> obj['.prop.'] = true;
>> obj['[prop]'] = true;
>>
>> print(obj['.prop.']);
>> print(obj['[prop]'])
>>
>> Your strategy is too complex
>
> But why would you EVER do that?

It can be necessary for mapping values that property names contain dots or
brackets.

> I'm not building this for idiots!

ISTM you are not building this for people who do DOM scripting either.
<form>...<input name="foo[]">...</form>

> [...]

>> I will prefer to use array which contain each property name. For example:
>> deep(context, ['property1', 'property2', 'propertyN']);
>
> How is that any better than joining the array with "." and passing a
> single string?!

The property names are clear.


PointedEars
--
var bugRiddenCrashPronePieceOfJunk = (
navigator.userAgent.indexOf('MSIE 5') != -1
&& navigator.userAgent.indexOf('Mac') != -1
) // Plone, register_function.js:16

Thomas 'PointedEars' Lahn

unread,
May 11, 2010, 5:13:00 PM5/11/10
to
Garrett Smith wrote:

> Asen Bozhilov wrote:
>> eval(properties);
>>
>> Of course that is not solve the design problems. I will prefer to use
>> array which contain each property name. For example:
>>
>> deep(context, ['property1', 'property2', 'propertyN']);
>
> Does deep check own or prototype?

You want to re-read the thread and re-think your question.

Scott Sauyet

unread,
May 11, 2010, 5:20:20 PM5/11/10
to

It's more general, not necessarily better. But it does address the
issues Thomas raises, and allows for the generality I was suggesting.
If your simple cases are all you ever need, by all means use a simpler
strategy.

It also has the advantage that you can pass variables holding strings
without manual concatenation. The only thing I would do differently
than the above is that I would remove the array and deal with variable
arguments; this seems cleaner to me:

deep(context, 'property1', 'property2', 'propertyN');

This still does not get at the even more general solution I mentioned,
and which I tried to implement some time ago. But of course that
solution had the problem yours does with property names containing
periods or brackets. I used an escape syntax for those, and that's
where some of the clean API went up in smoke.

I will see if I still have a copy of that code at home. I'm curious
to see how much five-year-old code might make me shudder.

--
Scott

Asen Bozhilov

unread,
May 11, 2010, 5:24:09 PM5/11/10
to
Matt Kruse wrote:
> Asen Bozhilov wrote:


> > I will prefer to use
> > array which contain each property name. For example:
> > deep(context, ['property1', 'property2', 'propertyN']);
>
> How is that any better than joining the array with "." and passing a
> single string?!

At all you miss the point and wrote in arrogant way that reply. Could
you show an implementation which works with:

var obj = {
property : {
'.property.' : {
'[property]' : true
}
}
};

$prop(obj, 'property[.property.][[property]]');

I expect `true' instead of `undefined'. So when you show that
implementation we can again talk about the complex of the problem
which you try to solve.

Matt Kruse

unread,
May 11, 2010, 5:28:21 PM5/11/10
to
On May 11, 4:20 pm, Scott Sauyet <scott.sau...@gmail.com> wrote:
> this seems cleaner to me:
>     deep(context, 'property1', 'property2', 'propertyN');

Would it handle this:

deep(context, 'prop1[0]')

if 'context' has no property named 'prop1'?
And what if 'context' has a property named 'myarray[0]' which is an
array?

deep(context, 'myarray[0][0]')

?

Matt Kruse

Garrett Smith

unread,
May 11, 2010, 5:30:33 PM5/11/10
to
Matt Kruse wrote:
> On May 11, 2:45 pm, Asen Bozhilov <asen.bozhi...@gmail.com> wrote:
>> Square bracket notation allow to used returned result by expression,
>> and that result is not bound as Identifiers rules defined by ECMA-262.
>> With your strategy you would have problems. For example:
>> var obj = {};
>> obj['.prop.'] = true;
>> obj['[prop]'] = true;
>>
>> print(obj['.prop.']);
>> print(obj['[prop]'])
>>
>> Your strategy is too complex
>
> But why would you EVER do that? I'm not building this for idiots!
>

Host:
navigator.plugins["Shockwave Flash 2.0"]?

User defined:
myObject[ prop.toString() ] = val;

[...]

Matt Kruse

unread,
May 11, 2010, 5:31:10 PM5/11/10
to
On May 11, 4:24 pm, Asen Bozhilov <asen.bozhi...@gmail.com> wrote:
> At all you miss the point and wrote in arrogant way that reply.

I don't think the reply was at all arrogant, but we may have a
separation in language.

> Could you show an implementation which works with:
> var obj = {
>   property : {
>     '.property.' : {
>       '[property]' : true
>     }
>   }
>
> };
> $prop(obj, 'property[.property.][[property]]');
> I expect `true' instead of `undefined'. So when you show that
> implementation we can again talk about the complex of the problem
> which you try to solve.

That is a more complex problem, yes. But it's one that _you_ are
proposing to solve, not me ;)

I will gladly limit the potential situations for which my solution
will apply, which for my case will probably cover 99.9% of the cases.
In the rare cases where it doesn't, I'm happier with writing context-
specific code rather than extending my solution to a more general,
obscure case.

Matt Kruse

Garrett Smith

unread,
May 11, 2010, 5:31:44 PM5/11/10
to
Thomas 'PointedEars' Lahn wrote:
> Garrett Smith wrote:
>
>> Asen Bozhilov wrote:

[...]

> You want to re-read the thread and re-think your question.

I want you to stop taking your personal frustrations out on this NG.

Thomas 'PointedEars' Lahn

unread,
May 11, 2010, 5:43:21 PM5/11/10
to
Matt Kruse wrote:

> Scott Sauyet wrote:
>> this seems cleaner to me:
>> deep(context, 'property1', 'property2', 'propertyN');
>
> Would it handle this:
>
> deep(context, 'prop1[0]')
>
> if 'context' has no property named 'prop1'?

No, for it would be looking for a property named `prop1[0]'. To look up the
`0' property of the object referred to by the `prop1' property, you would of
course call it so:

deep(context, 'prop1', 0)

> And what if 'context' has a property named 'myarray[0]' which is an
> array?
>
> deep(context, 'myarray[0][0]')
>
> ?

deep(context, 'myarray[0]', 0)

or

deep(context, ['myarray[0]', 0])

Which one you prefer would depend on the purpose. For example, for
JSX:isMethod/areMethods() I used additional arguments for property names
that are part of the same member expression, and a trailing additional array
argument for names of properties that are properties of the same object,
i.e.

jsx.object.areMethods(foo, 'bar', ['baz', 'bla'])

returns `true' iff both `foo.bar.baz' and `foo.bar.bla' refer to supposedly
callable objects that are referred to by properties of an object (in short:
methods).

Thomas 'PointedEars' Lahn

unread,
May 11, 2010, 5:45:49 PM5/11/10
to
Garrett Smith wrote:

> Thomas 'PointedEars' Lahn wrote:
>> Garrett Smith wrote:
>>> Asen Bozhilov wrote:
> [...]
>> You want to re-read the thread and re-think your question.
>
> I want you to stop taking your personal frustrations out on this NG.

It was merely a hint, stupid, and you managed to miss it.

Garrett Smith

unread,
May 11, 2010, 5:48:19 PM5/11/10
to
Thomas 'PointedEars' Lahn wrote:
> Garrett Smith wrote:
>
>> Thomas 'PointedEars' Lahn wrote:
>>> Garrett Smith wrote:
>>>> Asen Bozhilov wrote:
>> [...]
>>> You want to re-read the thread and re-think your question.
>> I want you to stop taking your personal frustrations out on this NG.
>
> It was merely a hint, stupid, and you managed to miss it.
>
That is exactly what I am talking about.

Thomas 'PointedEars' Lahn

unread,
May 11, 2010, 6:05:14 PM5/11/10
to
Garrett Smith wrote:

> Thomas 'PointedEars' Lahn wrote:
>> Garrett Smith wrote:
>>> Thomas 'PointedEars' Lahn wrote:
>>>> Garrett Smith wrote:
>>>>> Asen Bozhilov wrote:
>>> [...]
>>>> You want to re-read the thread and re-think your question.
>>> I want you to stop taking your personal frustrations out on this NG.
>> It was merely a hint, stupid, and you managed to miss it.
>>
> That is exactly what I am talking about.

Calling you stupid when you do stupid things is a telling a fact, not an
expression of any personal frustration I might have. So the problem is on
your part, not on mine.


PointedEars
--
Use any version of Microsoft Frontpage to create your site.
(This won't prevent people from viewing your source, but no one
will want to steal it.)
-- from <http://www.vortex-webdesign.com/help/hidesource.htm> (404-comp.)

Garrett Smith

unread,
May 11, 2010, 10:20:38 PM5/11/10
to
Thomas 'PointedEars' Lahn wrote:
> Garrett Smith wrote:
>
>> Thomas 'PointedEars' Lahn wrote:
>>> Garrett Smith wrote:
>>>> Thomas 'PointedEars' Lahn wrote:
>>>>> Garrett Smith wrote:
>>>>>> Asen Bozhilov wrote:
>>>> [...]
>>>>> You want to re-read the thread and re-think your question.
>>>> I want you to stop taking your personal frustrations out on this NG.
>>> It was merely a hint, stupid, and you managed to miss it.
>>>
>> That is exactly what I am talking about.
>
> Calling you stupid when you do stupid things is a telling a fact, not an
> expression of any personal frustration I might have. So the problem is on
> your part, not on mine.
>
That's your opinion based on your observations. My observation is that
Asen did not post code; I guess his `deep` does check prototype chain
but it's not something that's been mentioned yet.

Matt Kruse

unread,
May 12, 2010, 7:58:23 AM5/12/10
to
On May 11, 4:10 pm, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:

> > I'm not building this for idiots!
> ISTM you are not building this for people who do DOM scripting either.
> <form>...<input name="foo[]">...</form>

This already works fine:

$prop(document, "forms[0].foo[].value")

for example.

Matt Kruse

Matt Kruse

unread,
May 12, 2010, 8:01:15 AM5/12/10
to
On May 11, 4:24 pm, Asen Bozhilov <asen.bozhi...@gmail.com> wrote:
> Could you show an implementation which works with:
> var obj = {
>   property : {
>     '.property.' : {
>       '[property]' : true
>     }
>   }
>
> };
> $prop(obj, 'property[.property.][[property]]');
> I expect `true' instead of `undefined'. So when you show that
> implementation we can again talk about the complex of the problem
> which you try to solve.

Easy. I just modified the function to accept an optional last
parameter of delimiter.
So in this case you could just do:

$prop(obj, 'property|.property.|[property]', '|')

Maybe not as straight-forward as passing multiple arguments, but same
difference.

Matt Kruse

Thomas 'PointedEars' Lahn

unread,
May 12, 2010, 8:20:00 AM5/12/10
to
Matt Kruse wrote:

No, by contrast you will not be able to handle property names that contain
`|'.

Thomas 'PointedEars' Lahn

unread,
May 12, 2010, 8:21:13 AM5/12/10
to
Matt Kruse wrote:

> Thomas 'PointedEars' Lahn wrote:
>> > I'm not building this for idiots!
>> ISTM you are not building this for people who do DOM scripting either.
>> <form>...<input name="foo[]">...</form>
>
> This already works fine:
>
> $prop(document, "forms[0].foo[].value")
>
> for example.

You miss the point.

Matt Kruse

unread,
May 12, 2010, 9:26:03 AM5/12/10
to
On May 12, 7:20 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:

> No, by contrast you will not be able to handle property names that contain
> `|'.

Umm, then you just pick a different delimiter that doesn't conflict
with your property names. Duh.

Matt Kruse

Matt Kruse

unread,
May 12, 2010, 9:26:44 AM5/12/10
to
On May 12, 7:21 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:

> Matt Kruse wrote:
> > Thomas 'PointedEars' Lahn wrote:
> >> > I'm not building this for idiots!
> >> ISTM you are not building this for people who do DOM scripting either.
> >> <form>...<input name="foo[]">...</form>
> > This already works fine:
> > $prop(document, "forms[0].foo[].value")
> > for example.
> You miss the point.

On the contrary, you fail to make one.

Matt Kruse

David Mark

unread,
May 12, 2010, 9:38:29 AM5/12/10
to

And you still can't see why you are making a colossal design mistake?
Don't parse strings. Pass multiple arguments or an array of strings.
There you go.

David Mark

unread,
May 12, 2010, 9:39:39 AM5/12/10
to

No, as usual, you waste endless amounts of time "debating" when you
could be working. How do you ever get anything done?

Scott Sauyet

unread,
May 12, 2010, 9:52:41 AM5/12/10
to

Right, that is fairly obscure. But Thomas gave an example that is
quite realistic:

| <form>...<input name="foo[]">...</form>

A number of server-side environments might generate something like
that.

The question then is whether you want your solution to work for those
fairly common cases or if you are just as happy writing context-
specific code for them.

I did check for my old code last night and couldn't find anything from
that project. (It's not really mine anyway, but the client would
almost certainly give me permission to share it.) But it was single-
String-based like yours rather than multiple parameters as Thomas and
Asen are suggesting. And I did work with the more complex cases, but
as I did so, the code became more brittle and harder to use.
Eventually we abandoned it altogether.

If you want just the simple cases, then a single string is still okay.

But if you want to handle even reasonably common more complex cases,
then I would suggest that you use a multiple string parameter version,
whether in an array or not.

--
Scott

Asen Bozhilov

unread,
May 12, 2010, 10:08:14 AM5/12/10
to
Garrett Smith wrote:
> Asen Bozhilov wrote:

> > Of course that is not solve the design problems. I will prefer to use
> > array which contain each property name. For example:
>
> > deep(context, ['property1', 'property2', 'propertyN']);
>
> Does deep check own or prototype?

The meaning of _deep_ here is not related with prototype chain. The
deep here is used as deep level of property accessing instead. But if
I should implement that method I will use square bracket notation for
each property and that will lookup in prototype chain. I do not see
any reasons to handle only own properties.

Asen Bozhilov

unread,
May 12, 2010, 10:13:37 AM5/12/10
to
Matt Kruse wrote:

> Asen Bozhilov wrote:
>
> > Could you show an implementation which works with:
> > var obj = {
> >   property : {
> >     '.property.' : {
> >       '[property]' : true
> >     }
> >   }
>
> > };
> > $prop(obj, 'property[.property.][[property]]');

> Easy. I just modified the function to accept an optional last


> parameter of delimiter.
> So in this case you could just do:
>
> $prop(obj, 'property|.property.|[property]', '|')

That produce unreadable and unnatural code. If you want to use string
instead of list with properties, why not use just one notation? For
example you can use only dot notation and do restrictions on property
names cannot hold dot in their names. That will solve your problems,
and allow you to use simple code for parsing property names.

Matt Kruse

unread,
May 12, 2010, 10:19:42 AM5/12/10
to

I wouldn't say it's a colossal design mistake. It's a limitation, in
order to have the convenience of a single string to identify the
property. It's easier to read, and will work just fine almost all
common cases.

I could modify it to take any number of arguments, to support both
ways of accessing the properties - either as a single string with no .
in it, or as multiple strings with no limitations.

Matt Kruse


Scott Sauyet

unread,
May 12, 2010, 10:32:41 AM5/12/10
to
David Mark wrote:
> Matt Kruse wrote:
>> Thomas 'PointedEars' Lahn wrote:
>>> Matt Kruse wrote:
>>>> Thomas 'PointedEars' Lahn wrote:
>>>>>> I'm not building this for idiots!
>>>>> ISTM you are not building this for people who do DOM scripting either.
>>>>> <form>...<input name="foo[]">...</form>
>>>> This already works fine:
>>>> $prop(document, "forms[0].foo[].value")
>>>> for example.
>>> You miss the point.
>> On the contrary, you fail to make one.
> No, as usual, you waste endless amounts of time "debating" when you
> could be working.  How do you ever get anything done?

David,

Matt, who has spend far less time debating here in recent months than
either your or I, came here asking for discussion on an API he was
designing. He has certainly gotten that. He has, unsurprisingly,
been defending the decisions he's made, but he has not shown any signs
of being unwilling to listen to criticism. His only sign of testiness
was in his response to Thomas' bald assertion, "You miss the point."

Thomas has made some positive contributions to this thread. That post
was not among them, and I don't fault Matt for responding in a like
manner. Your only contribution of substance was another bald
assertion that his single-string API is a design mistake. You didn't
justify it at all. While I agree with you, based upon my own
experience with a similar API built some years back, your only posts
in this thread have been argumentative and not at all constructive.

It seems to me that it's you who's wasting time here.

(And now me too. Damn!)

--
Scott

Matt Kruse

unread,
May 12, 2010, 10:58:27 AM5/12/10
to
On May 12, 8:52 am, Scott Sauyet <scott.sau...@gmail.com> wrote:
> Right, that is fairly obscure.  But Thomas gave an example that is
> quite realistic:
> | <form>...<input name="foo[]">...</form>
> A number of server-side environments might generate something like
> that.

As I responded to him, the code in its current form works fine in this
case, or any case where a property has "[]" in it's name. It just
fails if the property has "[\d+]" in its name. I realize this also
happens in cases like:
<input name="person[0].phone">
which can be generated from frameworks like Struts, for example.

It's a limitation, certainly.

But IMO, a limitation does not necessarily mean a bad API or useless
function. It's pro/con.

If you can solve 99% of cases with straight-forward code which fails
on the remaining 1%, then I think it would be a mistake to through
that out just because you can't solve the most general case. That kind
of thinking leads to being less productive and insanity ;) But on the
flip side, I do think it's good to be as generalized as practical, and
that's exactly why I threw it out here. To get feedback and to point
out cons that I hadn't considered. For that, it has been useful
already.

Matt Kruse

Scott Sauyet

unread,
May 12, 2010, 11:01:01 AM5/12/10
to
Matt Kruse <m...@thekrusefamily.com> wrote:
> I could modify it to take any number of arguments, to support both
> ways of accessing the properties - either as a single string with no .
> in it, or as multiple strings with no limitations.

I would suggest you don't. Make your API as clean as possible while
still meeting your requirements and then live with whatever
restrictions that entails. Of all the critiques of jQuery I've seen
here, by far the most compelling is against the re-use of "$" to mean
so many different things.

--
Scott

Garrett Smith

unread,
May 12, 2010, 11:57:40 AM5/12/10
to
Asen Bozhilov wrote:
> Garrett Smith wrote:
>> Asen Bozhilov wrote:
>
>>> Of course that is not solve the design problems. I will prefer to use
>>> array which contain each property name. For example:
>>> deep(context, ['property1', 'property2', 'propertyN']);
>> Does deep check own or prototype?
>
> The meaning of _deep_ here is not related with prototype chain.

Obviously.

The
> deep here is used as deep level of property accessing instead. But if
> I should implement that method I will use square bracket notation for
> each property and that will lookup in prototype chain. I do not see
> any reasons to handle only own properties.
>

Makes sense.

Thomas 'PointedEars' Lahn

unread,
May 12, 2010, 12:32:54 PM5/12/10
to
Matt Kruse wrote:

> Thomas 'PointedEars' Lahn wrote:
>> No, by contrast you will not be able to handle property names that
>> contain `|'.
>
> Umm, then you just pick a different delimiter that doesn't conflict
> with your property names. Duh.

Your shortsightedness blinds you again for the possibilities that await you.
As long as you choose string delimiters for separating parts of the
/MemberExpression/ used to access a property, your implementation will not
be able to deal with objects that have properties with names that contain
those delimiters.

Matt Kruse

unread,
May 12, 2010, 1:06:51 PM5/12/10
to
On May 12, 11:32 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:

> Your shortsightedness blinds you again for the possibilities that await you.  
> As long as you choose string delimiters for separating parts of the
> /MemberExpression/ used to access a property, your implementation will not
> be able to deal with objects that have properties with names that contain
> those delimiters.

Thank you, Captain Obvious!

Be careful with these astounding leaps of logic. Soon you'll be making
conjectures like "The round ball is round". Sweet hot damn! Genius! :)

Matt "the key to picking a good delimiter is to use one that is not
contained in your string expression" Kruse

Thomas 'PointedEars' Lahn

unread,
May 12, 2010, 1:07:18 PM5/12/10
to
Matt Kruse wrote:

> Thomas 'PointedEars' Lahn wrote:
>> Matt Kruse wrote:
>> > Thomas 'PointedEars' Lahn wrote:
>> >> > I'm not building this for idiots!
>> >> ISTM you are not building this for people who do DOM scripting either.
>> >> <form>...<input name="foo[]">...</form>
>> > This already works fine:
>> > $prop(document, "forms[0].foo[].value")
>> > for example.
>> You miss the point.
>
> On the contrary, you fail to make one.

It is not that hard to miss, see <news:2882225.e...@PointedEars.de>.

Thomas 'PointedEars' Lahn

unread,
May 12, 2010, 1:13:53 PM5/12/10
to
Matt Kruse wrote:

> Thomas 'PointedEars' Lahn wrote:
>> Your shortsightedness blinds you again for the possibilities that await
>> you. As long as you choose string delimiters for separating parts of the
>> /MemberExpression/ used to access a property, your implementation will
>> not be able to deal with objects that have properties with names that
>> contain those delimiters.
>

> [...]


> Matt "the key to picking a good delimiter is to use one that is not
> contained in your string expression" Kruse

How naive are you anyway? *Any* Unicode character may be contained in a
property name. The only way for you to work around that problem is to
provide a way for the user of the function/library to define the
delimiter(s) before or upon the call and to make the provision that property
names of the object do not contain it/them.

However, no such provision is necessary and it it is more efficient, a lot
more simple and compatible not to use a string delimiter that you need to
split on in the first place.

Scott Sauyet

unread,
May 12, 2010, 1:16:42 PM5/12/10
to
Thomas 'PointedEars' Lahn wrote:
> Matt Kruse wrote:
>> Thomas 'PointedEars' Lahn wrote:
>>> No, by contrast you will not be able to handle property names that
>>> contain `|'.
>
>> Umm, then you just pick a different delimiter that doesn't conflict
>> with your property names. Duh.
>
> Your shortsightedness blinds you again for the possibilities that await you.  
> As long as you choose string delimiters for separating parts of the
> /MemberExpression/ used to access a property, your implementation will not
> be able to deal with objects that have properties with names that contain
> those delimiters.

While I agree with what seems to be the majority opinion that strings
with delimiters are not the best solution, I'm not sure I can buy this
objection. Matt said he was passing alternative delimiters as an
additional parameter to the call. Since the presumed use is something
like

var xyz = $prop(document, "forms[0].foo[].value");

or if necessary,

var xyz = $prop(obj, 'property|.property.|[property]', '|');

and not

var xyz = $prop(obj, myStringVar);

it's easy enough to see by inspection that the delimiter is not
included in the string. The point is that this is to be used for
known source code and data structures; it's simply a mechanism to
avoid the nested chains of checking on the existence of intermediate
properties on the chain. It's not, in my understanding, an attempt to
provide a general-purpose property-fetching API for arbitrary data
structures.

If you've used a Java template engine such as Velocity or Freemarker,
it would be an attempt at something similar to the dotted property
access notation that they provide. Instead of code like:

public String fetch(MyClass myObj) {
if (myObj != null) {
SomeClass path = myObj.getPath();
if (myPath != null) {
AnotherClass to = myPath.getTo();
if (to != null) {
String property = to.getProperty();
if (property != null) {
return property;
}
}
}
}
return "";
}


these tools allow access the property like this:

${myObj.path.to.property}


The big difference in ECMASCript is that the period is allowed as part
of property identifiers. If Matt's code allowed you to, if necessary,
use this instead:

$prop(myObj "path~to~property", "~")

I simply don't see the issue.

-- Scott

Thomas 'PointedEars' Lahn

unread,
May 12, 2010, 2:00:17 PM5/12/10
to
Scott Sauyet wrote:

Utter nonsense.

First of all, to $prop() there is exactly no difference with regard to how
the value was passed. That's not only JavaScript 101, it's Programming 101.

Second, without further information the first property access could be the
equivalent of any of the following, with the list not making a claim to be
complete:

document.forms[0].foo.["[]"].value
document.forms[0]["foo[]"].value
document.forms["[0].foo[]"].value
document["forms[0]"]["foo[]"].value
document["forms[0].foo[]"].value
document["forms[0]"]["foo[].value"]
document["forms[0].foo[].value"]

With the second approach, it needs to be clearly defined which delimiter is
used if no delimiter is specified.

Third, with either solution the issue remains that a property name may not
contain the delimiter then, when that would not be necessary had a different
approach been chosen. IOW, it is not possible to access

obj["property"][".property."]["[prop|erty]"]

with

$prop(obj, 'property|.property.|[prop|erty]', '|')

because that would access

obj["property"][".property."]["[prop"]["erty]"]

instead. And if you defined `[' and `]' (or something else) as delimiter
for property names you would could not have property names that contained
them. Or your would have to implement an escape syntax so that those
characters are not considered delimiters, which would exclude those escape
sequences from being part of property names.

But all of this is completely unnecessary if you let the programming
language provide the delimiter, i.e. arguments, Arrays, or a combination
thereof.


PointedEars
--
realism: HTML 4.01 Strict
evangelism: XHTML 1.0 Strict
madness: XHTML 1.1 as application/xhtml+xml
-- Bjoern Hoehrmann

Thomas 'PointedEars' Lahn

unread,
May 12, 2010, 2:10:32 PM5/12/10
to
Thomas 'PointedEars' Lahn wrote:

> Scott Sauyet wrote:
>> var xyz = $prop(document, "forms[0].foo[].value");

>> [...]
>
> [...] without further information the first property access could be the


> equivalent of any of the following, with the list not making a claim to be
> complete:
>
> document.forms[0].foo.["[]"].value

document.forms[0].foo["[]"].value

> document.forms[0]["foo[]"].value
> document.forms["[0].foo[]"].value
> document["forms[0]"]["foo[]"].value
> document["forms[0].foo[]"].value
> document["forms[0]"]["foo[].value"]
> document["forms[0].foo[].value"]

> [...]

Matt Kruse

unread,
May 12, 2010, 2:23:34 PM5/12/10
to
On May 12, 1:00 pm, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:

> Third, with either solution the issue remains that a property name may not
> contain the delimiter then, when that would not be necessary had a different
> approach been chosen.  IOW, it is not possible to access
>   obj["property"][".property."]["[prop|erty]"]
> with
>   $prop(obj, 'property|.property.|[prop|erty]', '|')
> because that would access
>   obj["property"][".property."]["[prop"]["erty]"]

Are you being intentionally obtuse?

$prop(obj, 'property/.property./[prop|erty]', '/')
or
$prop(obj, 'property#.property.#[prop|erty]', '#')
or
$prop(obj, 'propertySHUTUPTHOMAS.property.SHUTUPTHOMAS[prop|erty]',
'SHUTUPTHOMAS')

Take your pick.

I agree this is not the best syntax. I proposed it as a solution only
to counter your nit-picking about dots in property names.

In reality, if the properties contained such characters, I would
either not use the function or I would modify it, or perhaps make an
alternative version that takes arguments as parameters.

Matt Kruse

Thomas 'PointedEars' Lahn

unread,
May 12, 2010, 2:50:27 PM5/12/10
to
Matt Kruse wrote:

> Thomas 'PointedEars' Lahn wrote:
>> Third, with either solution the issue remains that a property name may
>> not contain the delimiter then, when that would not be necessary had a
>> different approach been chosen. IOW, it is not possible to access
>> obj["property"][".property."]["[prop|erty]"]
>> with
>> $prop(obj, 'property|.property.|[prop|erty]', '|')
>> because that would access
>> obj["property"][".property."]["[prop"]["erty]"]
>
> Are you being intentionally obtuse?

No, you are. The issue is fairly obvious, but you choose to ignore it.



> $prop(obj, 'property/.property./[prop|erty]', '/')
> or
> $prop(obj, 'property#.property.#[prop|erty]', '#')
> or

So property names containing `/' and `#' could not be accessed then, i.e.
neither

$prop(obj, 'property/.property./[prop#/erty]', '/')

nor

$prop(obj, 'property#.property.#[prop#/erty]', '#')

could access e.g.

obj["property#"][".prop/erty."]["#[prop#/erty]"]

aso.

And if you used an array of delimiters instead of only one delimiter, or a
regular expression, the matched characters could not be contained in a
property name.

> $prop(obj, 'propertySHUTUPTHOMAS.property.SHUTUPTHOMAS[prop|erty]',
> 'SHUTUPTHOMAS')

Shut up yourself and power up your brain for a change.

> Take your pick.

I will continue to choose the rational, logical course of action;
so not yours.



> I agree this is not the best syntax.

You are a master of understatement. Your $prop() will be unable to access a
property if any property name along the way contains any designated
delimiter. That way you are limiting its capabilities. Needlessly.


Score adjusted

PointedEars
--
Anyone who slaps a 'this page is best viewed with Browser X' label on
a Web page appears to be yearning for the bad old days, before the Web,
when you had very little chance of reading a document written on another
computer, another word processor, or another network. -- Tim Berners-Lee

Stefan Weiss

unread,
May 12, 2010, 2:52:23 PM5/12/10
to
On 11/05/10 15:44, Matt Kruse 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?
>
> For example:
>
> deep(document, "body.firstChild.style.backgroundColor");
> or
> deep("myElementId.childNodes[3].id");
> or
> deep( myJsonObject,
> "locations.ca.sandiego.directory.people.smith.john.phone");
>
> This would return undefined if any element along the chain is not
> there, or index is out of bounds, etc. If the chain gets to the end,
> it would return the last-evaluated value. This would be convenient for
> accessing deep properties of objects that may or may not exist, and
> not having to manually check the chain in your code.

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].


--
stefan

Scott Sauyet

unread,
May 12, 2010, 3:05:03 PM5/12/10
to
Thomas 'PointedEars' Lahn wrote:
> Scott Sauyet wrote:
>> Since the presumed use is something like
>>     var xyz = $prop(document, "forms[0].foo[].value");
>> or if necessary,
>>     var xyz = $prop(obj, 'property|.property.|[property]', '|');
>> and not
>>     var xyz = $prop(obj, myStringVar);
>> it's easy enough to see by inspection that the delimiter is not
>> included in the string.
>
> Utter nonsense.
>
> First of all, to $prop() there is exactly no difference with regard to how
> the value was passed.  That's not only JavaScript 101, it's Programming 101.

That's correct. And similarly, an API like
document.getElementById(value) does not prohibit you from supplying a
number or the window object as the value parameter. But those are
still not good ideas. To me an API like this is something more like a
macro than a function call. Of course someone could misuse it. But
that's not enough of a reason for discarding it. The only reason I
would have for using it would be to make my code simpler. I would
never use it with anything other than a string literal. If the
language had a facility to prevent the user from passing something
else, I would take advantage of it; as it stands, its limitations
would have to be documented.

Perhaps it was later in Programming 101 that students learn that not
everything you might want to do is supplied by the language or the
rest of the programming environment, that you might have to extend
that environment with your own code, and that sometimes that code has
to make compromises between simplicity and completeness.


> [ ... ]


> But all of this is completely unnecessary if you let the programming
> language provide the delimiter, i.e. arguments, Arrays, or a combination
> thereof.

It's not that I disagree. But I think your arguments are overstated.
If Matt is going to publish this and promote it as an amazing tool
that will drastically increase the productivity of any web developer,
then he really should skip the string parsing and go with the more
complete API. But if he is going to use this on a few projects with a
half-dozen other coders, I would suggest that simplicity should rule.

--
Scott

Matt Kruse

unread,
May 12, 2010, 3:37:12 PM5/12/10
to
On May 12, 1:50 pm, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:

> So property names containing `/' and `#' could not be accessed then, i.e.
> neither
>    $prop(obj, 'property/.property./[prop#/erty]', '/')
> nor
>   $prop(obj, 'property#.property.#[prop#/erty]', '#')
> could access e.g.
>   obj["property#"][".prop/erty."]["#[prop#/erty]"]

Are you missing the basic point that you PICK the delimiter based on
the string you are passing in?

1. Pick your property string to evaluate
2. If the properties contain . or [ or ], then pick a delimiter
3. Choose the delimiter to be a character or sequence of characters
that does not exist in your property string

Is that so hard to understand?

Similar to perl, where you can do:

$var =~ s/a/b/;
or
$var =~ s|a/b|c/d|;
or
$var =~ s#a/b#c/d#;

Nevertheless, your examples are sufficiently insane to not warrant
consideration in any real case that I will ever expect to encounter.

Matt Kruse

Matt Kruse

unread,
May 12, 2010, 3:38:52 PM5/12/10
to
On May 12, 1:52 pm, Stefan Weiss <krewech...@gmail.com> wrote:
> 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].

Yes indeed, in fact some of the code I have has that littered all
over. It makes it harder to read, IMO. But it certainly has the
benefits you describe.

I'm also not familiar enough with the performance hit of try/catch to
know if that's even a factor. In some versions of some languages, it
can be quite costly to enter a try/catch block.

Matt Kruse

Thomas 'PointedEars' Lahn

unread,
May 12, 2010, 3:55:16 PM5/12/10
to
Scott Sauyet wrote:

> Thomas 'PointedEars' Lahn wrote:
>> [...]


>> But all of this is completely unnecessary if you let the programming
>> language provide the delimiter, i.e. arguments, Arrays, or a combination
>> thereof.
>
> It's not that I disagree. But I think your arguments are overstated.
> If Matt is going to publish this and promote it as an amazing tool
> that will drastically increase the productivity of any web developer,
> then he really should skip the string parsing and go with the more
> complete API. But if he is going to use this on a few projects with a
> half-dozen other coders, I would suggest that simplicity should rule.

And exactly therefore string parsing is a really bad idea here, for it
*complicates* matters *needlessly*. It requires the caller to think hard
about what delimiter can be used, in fact it requires them to parse the
argument by *themselves*, instead of just passing the arguments and let the
function do all the work. I fail to see why it should be simpler to call

$prop(obj, 'prop/erty..prop#erty..[prop|erty]')

no, wait, the dot is already used, must be

$prop(obj, 'prop/erty|.prop#erty.|[prop|erty]', '|')

no, wait, there's a pipe in a name, so must be

$prop(obj, 'prop/erty#.prop#erty.#[prop|erty]', '#')

no, wait, that darn hash is also used, must be

$prop(obj, 'prop/erty/.prop#erty./[prop|erty]', '/')

no, wait, that f***ing slash is already used, too, so must be

$prop(obj, 'prop/erty%.prop#erty.%[prop|erty]', '%')

-- and let's hope that we don't have overlooked a cursed `%' somewhere in
the real case -- instead of simply

$prop(obj, 'prop/erty', '.prop#erty.', '[prop|erty]')

or

$prop(obj, ['prop/erty', '.prop#erty.', '[prop|erty]'])

or a combination thereof.

So please forgive me if I really fail to see that kind of simplicity you are
talking about with the string-parsing approach.

Garrett Smith

unread,
May 12, 2010, 3:57:53 PM5/12/10
to
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? You could put a comment:

// Use try catch in case body.firstChild does not have a style property.


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

That's worse, because now the code comment has to explain the failure of
the code to explain itself -- and for something so trivial and basic as
getting a simple property.

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.

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

Thomas 'PointedEars' Lahn

unread,
May 12, 2010, 3:58:28 PM5/12/10
to
Matt Kruse wrote:

> Thomas 'PointedEars' Lahn wrote:
>> So property names containing `/' and `#' could not be accessed then, i.e.
>> neither $prop(obj, 'property/.property./[prop#/erty]', '/')
>> nor $prop(obj, 'property#.property.#[prop#/erty]', '#')
>> could access e.g.
>> obj["property#"][".prop/erty."]["#[prop#/erty]"]
>
> Are you missing the basic point that you PICK the delimiter based on
> the string you are passing in?

No, you are missing the point that this requires the caller to determine the
delimiter, i.e. parse the argument themselves, and the callee to parse it
again, when that is not necessary otherwise.


PointedEars
--
Danny Goodman's books are out of date and teach practices that are
positively harmful for cross-browser scripting.
-- Richard Cornford, cljs, <cife6q$253$1$8300...@news.demon.co.uk> (2004)

Garrett Smith

unread,
May 12, 2010, 4:10:08 PM5/12/10
to
Thomas 'PointedEars' Lahn wrote:
> Scott Sauyet wrote:
>
>> Thomas 'PointedEars' Lahn wrote:
>>> [...]
>>> But all of this is completely unnecessary if you let the programming
>>> language provide the delimiter, i.e. arguments, Arrays, or a combination
>>> thereof.
>> It's not that I disagree. But I think your arguments are overstated.
>> If Matt is going to publish this and promote it as an amazing tool
>> that will drastically increase the productivity of any web developer,
>> then he really should skip the string parsing and go with the more
>> complete API. But if he is going to use this on a few projects with a
>> half-dozen other coders, I would suggest that simplicity should rule.
>
> And exactly therefore string parsing is a really bad idea here, for it
> *complicates* matters *needlessly*.

Yep. Matt's is needlessly complex and burdens the caller with dealing
with the delimiter.

deepMatt(obj, "p1.p2", ".");
deepAsen(obj, ["p1", "p2"]);

Both create an array.

`deepMatt`
- parses the string, splitting on delimiter
- requires caller to supply delimiter as a third argument.
- can fail with unknown property name (which may contain delimiter).

`deepAsen`
- requires caller to supply an array.

One benefit for Matt's is that it looks more like a normal property
access when only "." is used.

The deepAsen function is simpler and easier to use. This is a big
advantage over deepMatt. Having the flexibility of dynamic property name
might be an advantage in some cases, such as seen with square-bracket
property accessors, so that might be another benefit realized later.

VK

unread,
May 12, 2010, 4:30:59 PM5/12/10
to
On May 11, 7:52 pm, Matt Kruse <m...@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.

Stefan Weiss

unread,
May 12, 2010, 4:34:07 PM5/12/10
to
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