Brendan, you've asked for other coding examples where I use the pattern of
some variable being `undefined` or not to trigger different behavior (that
is, to use the variable or not). Here's two more:
1. I have a templating engine DSL (called HandlebarJS) I wrote (in JS),
which includes a very strict minimal subset of a "JavaScript-like" syntax
for declaring template variables in the definition-header of a template
section. In fact, the variable declaration stuff is massaged just slightly
(for variable namespacing, mostly), and then executed as straight
JavaScript.
In a template section header, I'm able to define a "local variable" for the
section which has the ID reference of another sub-template section to
include. If you ask to include a sub-template, and the "local variable" you
give as the name of the template to include is an empty "" or is `undefined`
or otherwise falsy, then the templating engine simply skips over that
template section inclusion. Example:
{$: "#main" | content = data.content ? "#content" : "" $}
<h1>Hello World</h1>
{$= @content $}
{$}
{$: "#content"}
<p>
{$= data.content $}
</p>
{$}
So, if the `data.content` variable is truthy, then the local variable
`content` gets value "#content" (a string ID reference to the sub-template
section of that name), and if not, then it ends up as the empty string "".
Then, after the <h1> tag, a template-include is called, with {$= @content
$}, which basically says, get the value out of that local variable, and if
it's a reference (by ID) to a template section, then include it. If the
variable referenced is empty, or undefined, or null, or whatever falsy, then
simply silently don't include anything.
Of course, the `: ""` part of the variable declaration is what's relevant to
our discussion. I could obviously specify another template ID in that
string... but in this example I'm giving, if the `data.content` is empty, I
don't want to include the markup (the <p>...</p>) from the #content template
at all, so I just set the local variable to "", which results in no
sub-template being included.
Here's where this gets to the point: in my template DSL, you're allowed to
drop the `: ""` from ?: usage, so the declaration for main can look like
slightly cleaner, like this:
{$: "#main" | content = data.content ? "#content" $}
Purely syntax sugar I'm giving there, to help the template not have so much
"code" in it. If the "" or `undefined` or whatever falsy value in the "or"
case can be assumed, it makes the writing of that variable declaration a
little bit nicer. When I parse the data declarations from my template
syntax, and then hand it off to JavaScript, I simply substitute in the `:
undefined` if it's not present. I even take care of nested ?: with the
optional `:` parts.
To put a finer point on it, it would be nicer/easier if I didn't have to add
those implied clauses in, because the JavaScript language just supported
that syntactic sugar directly.
-------------------------
2. I've got cases where I have a set of code in an embeddable widget, that
someone can take the code and embed into their site. My code relies on
jQuery, but at least jQuery 1.4. So, I first check to see if jQuery is
already present, and if it's 1.4+, and then if so, I just use the page's
copy of jQuery. Otherwise, I go ahead and load jQuery dynamically for my
code to use.
So, a drastically simplified version (yes, I know the version matching logic
is faulty from the over-simplification) of this code looks like this:
(function(){
var jq = ($ && $.fn.jquery.match(/^1\.[4-9]/)) ? $ : undefined;
...
if (typeof jq == "undefined") {
// go ahead and load jQuery dynamically
}
})();
So, how can I write this code differently? Of course there's other ways to
write it.
var jq;
($ && $.fn.jquery.match(/^1\.[4-9]/)) && jq = $; // jq is either jQuery or
it's still `undefined`, which I prefer
OR
var jq = ($ && $.fn.jquery.match(/^1\.[4-9]/)) ? $ : null; // jq is either
jQuery or it's `null`, which I like less
OR
var jq = ($ && $.fn.jquery.match(/^1\.[4-9]/)) && $; // jq is either jQuery
or it's `false`, which I like less
OR .......
And there's probably half a dozen other patterns too. I could use `null` as
the sentinel value, I could use `false`, heck I could even use an empty ""
string.
The point is, I prefer to use `undefined` in such cases, because
semantically, I'm saying that my `jq` is in fact "undefined" (or rather,
"not yet defined") if either jQuery is not present, or the version match
fails. I don't prefer to use `false` or `null` or `0` or `""` as the
alternate value, I prefer to use `undefined`.
And so, it'd be nice if my `var` statement could be simpler, like:
var jq = ($ && $.fn.jquery.match(/^1\.[4-9]/)) ? $;
Why? because it keeps the declaration and initialization all in one
statement, which is cleaner, and because it preserves that `jq` is only ever
a valid reference to jQuery, or it's strictly `undefined`... there's no
other values I have to worry about it being. That also means that the check
later is `typeof jq == "undefined"`, which I consider to be most semantic,
rather than `typeof jq != "object"` (which fails if I use `null`) or `jq !=
null` (which works for either `null` or `undefined`, but in my opinion is
less semantic).
-------------------------
In any case, set the `doX` argument from earlier aside, and there's still
other examples of how I like to use the semantic meaning of `undefined` for
signaling that a variable is not yet initialized, and that the code needs to
react accordingly if so. You may disagree entirely with both above usages as
well, but the point is that it's not some limited one-off niche use case,
it's a pattern that I find useful across a variety of projects, and one
which I wish had just a tiny bit more semantic sugar to it.
--Kyle
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss