Hey Juriy,

> I respect your style, T.J., but things like "return undefined" always
> throw me off as being a bit redundant : ) Functions return undefined
> implicitly when nothing is specified, so why not take advantage of
> that?

I take your point, but it's a clarity thing.  My background is
overwhelmingly in compiled languages.  I probably should have written:

    return /*undefined*/;

...since I'd pass all of this stuff through a packer/minifier anyway.

<OT>
The more serious JavaScript coding I do, the more I want a compiler.
Source clarity *matters* to me, source malleability *matters* to me.
I don't want a compiler in the sense of machine code or bytecode,
really, but in the sense of:  There's a version for people, and
there's a version for machines, and the imperatives are different.
Packers and minifiers help -- they certainly put the shame on people
who don't comment -- but only so far. :-)
</OT>

-- T.J. ;-)

On Oct 23, 5:30 pm, kangax <[EMAIL PROTECTED]> wrote:
> On Oct 22, 5:42 am, "T.J. Crowder" <[EMAIL PROTECTED]> wrote:
>
>
>
> > @All:
>
> > You know how you plan to send a really brief, simple reply and things
> > just get out of hand?  Apologies for the long post...
>
> > @RobG:
>
> > > doSomething($('elID'))
>
> > I think he means using Prototype's element extensions (though I could
> > be mistaken).  Naturally if you like, you can code your own methods to
> > handle being passed an undefined element.  But while you can do this:
>
> > Element.toggle('elID');
>
> > ...it will still throw an error, as most of Prototype's functions
> > don't guard against undefined elements.  (*Appropriately* don't, in my
> > view; to me 90% of the time that's an application logic error.)
>
> > @OP:
>
> > If you really want this "nullsafe" behavior, there are at least three
> > ways I can think of to get it, two of which come with a significant
> > runtime cost:
>
> > 1. Modify your copy of Prototype.js:
>
> > Figure you'll have to do this about twice a year as you deploy new
> > versions of Protoype, but that's not that bad, is it?  Basically
> > taking (say) Element.toggle:
>
> >   toggle: function(element) {
> >     element = $(element);
> >     Element[Element.visible(element) ? 'hide' : 'show'](element);
> >     return element;
> >   },
>
> > ...and inserting some code to fail silently if the element doesn't
> > exist:
>
> >   toggle: function(element) {
> >     element = $(element);
> >     if (!element) return undefined; // <=== New code
> >     Element[Element.visible(element) ? 'hide' : 'show'](element);
> >     return element;
> >   },
>
> > That means that "Element.toggle('elID')" will fail silently rather
> > than raising an error.  (Naturally "$('elID').toggle()" will still
> > raise an error, since that's trying to call a function on an undefined
> > reference.)
>
> > 2. Be explicit with a wrapper
>
> > In my view, in the normal case if you're passing an undefined
> > reference, it's an application logic error and so you *want* Prototype
> > to throw an error.  But there are certainly other cases where the
> > thing you want to act on can *correctly* either exist or not.  So why
> > not be explicit about it in your code:
>
> > function nscall(func, element) {
> >     var args;
>
> >     element = $(element);
> >     if (!element)
> >     {
> >         return undefined;
> >     }
> >     args = $A(arguments);
> >     args.shift();
> >     args[0] = element;
> >     return func.apply(this, args);
>
> > }
>
> I respect your style, T.J., but things like "return undefined" always
> throw me off as being a bit redundant : ) Functions return undefined
> implicitly when nothing is specified, so why not take advantage of
> that?
>
> My biased little version would be among these lines:
>
> function nscall(func, element) {
>   if (element = $(element)) {
>     return func.apply(null, $A(arguments).slice(1));
>   }
>
> }
>
> We could also replace $A with `Array.prototype.slice.call` as a minor
> speed improvement.
>
>
>
>
>
> > In places where it's valid that the element may not exist, instead of:
>
> >     $('elID').toggle();  or  Element.toggle('elID');
>
> > ...it would be
>
> >     nscall(Element.toggle, 'elID');
>
> > You're being clear that it's okay if the element doesn't exist.
>
> > The runtime cost here is obviously several additional function calls,
> > both to nscall() and within it.
>
> > 3. Wrap Prototype's Functions
>
> > Finally and most intrusively, you can always wrap the functions for
> > which you want the behavior, using Function.wrap[1]:
>
> > Element.toggle = Element.toggle.wrap(function(originalFunction,
> > element) {
> >     element = $(element);
> >     return element ? originalFunction(element) : undefined;
>
> > });
>
> > That makes "Element.toggle('elID')" fail quietly if there is no
> > element with the ID 'elID'.  (Again, the methodized version "$
> > ('elID').toggle()" would of course still fail.)
>
> > You'd have to do this for all functions that you want to have behave
> > that way, but I'm guessing it's probably a subset of the full Element
> > API and even if not, that API doesn't change so frequently that it's a
> > massive issue updating your list when you deploy a new Prototype
> > version.  But if you REALLY want to make it easy, here's a barely-
> > tested function that takes a space-delimited list of Element methods
> > and wraps them to make them fail silently if the element is undefined:
>
> > $w('toggle addClassName').each(function(fname) {
> >     Element[fname] = Element[fname].wrap(function(originalFunction,
> > element) {
> >         var args;
>
> >         element = $(element);
> >         if (!element)
> >         {
> >             return undefined;
> >         }
> >         args = $A(arguments);
> >         args.shift();
> >         args[0] = element; // We've already looked it up, save doing
> > it again
> >         return originalFunction.apply(this, args);
> >     });
>
> > });
>
> > (In that example, I've only wrapped 'toggle' and 'addClassName' --
> > although I think addClassName already fails silently.)
>
> > Breaking that down, we use the $w() utility function[2] to turn the
> > space-delimited list into an Array, then we use Enumerable.each[3]
> > (which is mixed into Array) to process each item in that Array.
> > Inside our iterator, Element[fname] references the function, and so we
> > wrap each of those using the given wrapper.  The wrapper checks if the
> > element exists and if so, passes on the element and any other
> > arguments to the original function.  (Note the args.shift(), since of
> > course the first argument to our wrapper is the original function.)
>
> > Again, the runtime cost there is significant and I don't think I'd do
> > it.
>
> I think `wrap` option is least obtrusive of the ones you presented. I
> also think that `if (foo) bar(...)` (or, perhaps, `foo && bar(...)`)
> is the best way to handle `null`/`undefined` values. Best from the
> performance, clarity and maintenance points of view.
>
> On the other hand, this problem somewhat disappears once element
> wrappers are introduced (as the ones we had lengthy discussions about
> in the past). When every method is guaranteed to return a reference to
> a wrapper - existence of an element/collection is of little importance
> (besides runtime/memory costs)
>
>
>
> > [1]http://www.prototypejs.org/api/function/wrap
> > [2]http://www.prototypejs.org/api/utility/dollar-w
> > [3]http://www.prototypejs.org/api/enumerable/each
>
> > HTH,
> > --
> > T.J. Crowder
> > tj / crowder software / com
>
> [snip]
>
> --
> kangax
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Prototype & script.aculo.us" group.
To post to this group, send email to prototype-scriptaculous@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/prototype-scriptaculous?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to