Archives
-
DNN World 2011
We’re on the plane flying back to St. Louis from DNN World 2011. I gave a presentation titled DNN 6 UI/UX Patterns, discussing the form patterns introduced in the administrative modules in DNN 6 (the new look and feel that you immediately noticed after logging into your new DNN 6 site). Many folks asked about seeing the examples that I presented, and they are available as a repository on github, at https://github.com/bdukes/DNN-World-Demos. This includes a series of small, one-control modules that demonstrate the various parts and pieces introduced in DNN 6. I’ve also placed the slide deck on SlideShare, (though the vast majority of the content was just demonstration, don’t be expecting a wealth of information there). Feel free to let me know if you have any questions, and I’ll do my best to give clarifications.
-
JavaScript: true, false, and in between
In JavaScript, all values can be coerced into a Boolean value (i.e.
true
orfalse
). Most values will evaluate totrue
, there are just a handful that will befalse
. Aside from the literalfalse
value, the number zero (0
), an empty string (''
),NaN
,null
, andundefined
all evaluate tofalse
in a Boolean context. All other values will evaluate to true (even an empty object ({}
) or an empty array ([]
) or ajQuery
selector that didn’t select anything). So, what contexts cause this Boolean evaluation? When am I actually going to see this in action?Most commonly, you’ll see this when using values in an
if
statement.var input = $('#text').val(); if (input) { // only do this if input is not an empty string }
So, when you see a non-Boolean value as the condition of an
if
statement, you can translate that in your head to “if that expression has a value,” so long as you remember that empty objects “have a value,” too. Pretty easy once you know what you’re seeing.Now, because all values can act as Booleans, operators that you might expect to return a Boolean result don’t necessarily have to return an explicit Boolean. As an example:
var text = ''; var index = 10; var hasTextOrIndex = text || index;
If the
||
operator was in the business of returning Boolean values, we might expecthasTextOrIndex
to betrue
; however, while it’strue
enough to get us into the body of anif(hasTextOrIndex)
statement, the actual value ofhasTextOrIndex
is10
. The definition of the||
operator is that it returns the left side if it can be converted totrue
, and otherwise returns the right side (&&
returns the left side if it can be converted tofalse
, and otherwise returns the right side). In the Boolean context, this behaves like the logical operators we’re used to, but lets us use them in non-Boolean ways as well. Most commonly, you’ll see||
used as a null-coalescing operator (like C#’s??
), saying “give me the left side unless it’sundefined
, otherwise give me a fallback value.” Less commonly, you’ll see&&
used to access an object’s property or method when that object might beundefined
(I don’t find it particularly readable, especially with methods, but it’s something you’ll see in the wild, if not something you’ll be using yourself).jQuery.fn.myPlugin = function (timeout) { var timeoutOption = timeout || 500; window.console && window.console.log('timeout is ', timeoutOption); }
The big caution here is to remember that
0
,NaN
, and''
are falsy values, so you need to be sure that0
isn’t a valid timeout value if you’re going to use the above example. Same thing goes withif
statements; remember, when you think “has a value” you need to think “isn’t undefined, zero, an empty string, or NaN or null”. -
JavaScript: this
JavaScript is a language steeped in juxtaposition. It was made to “look like Java,” yet is dynamic and classless. From this origin, we get the
new
operator and thethis
keyword. You are probably used tothis
referring to the current instance of a class, so what could it mean in a language without classes?In JavaScript,
this
refers to the object off of which a function is referenced when it is invoked (unless it is invoked viacall
orapply
). What this means is thatthis
is not bound to your function, and can change depending on how your function is invoked. It also means thatthis
changes when declaring a function inside another function (i.e. each function has its ownthis
), such as when writing a callback. Let's see some of this in action:var obj = { count: 0, increment: function () { this.count += 1; }, logAfterTimeout = function () { setTimeout(function () { console.log(this.count); }, 1); } }; obj.increment(); console.log(obj.count); // 1 var increment = obj.increment; window.count = 'global count value: '; increment(); console.log(obj.count); // 1 console.log(window.count); // global count value: 1 var newObj = {count:50}; increment.call(newObj); console.log(newObj.count); // 51 obj.logAfterTimeout();// global count value: 1 obj.logAfterTimeout = function () { var proxiedFunction = $.proxy(function () { console.log(this.count); }, this); setTimeout(proxiedFunction, 1); }; obj.logAfterTimeout(); // 1 obj.logAfterTimeout = function () { var that = this; setTimeout(function () { console.log(that.count); }, 1); }; obj.logAfterTimeout(); // 1
The last couple of examples here demonstrate some methods for making sure you get the values you expect. The first time
logAfterTimeout
is redefined, we usejQuery.proxy
to create a new function which has itsthis
permanently set to the passed in value (in this case, the currentthis
). The second timelogAfterTimeout
is redefined, we save the value ofthis
in a variable (namedthat
in this case, also often namedself
) and use the new variable in place ofthis
.Now, all of this is to clarify what’s going on when you use
this
. However, it’s pretty easy to avoid usingthis
altogether in your code (especially in the way I’ve demonstrated above). Instead of usingthis.count
all over the place, it would have been much easier if I’d madecount
a variable instead of a property, and then I wouldn’t have to usethis
to refer to it.var obj = (function () { var count = 0; return { increment: function () { count += 1; }, logAfterTimeout = function () { setTimeout(function () { console.log(count); }, 1); }, getCount: function () { return count; } }; }());
If you’re writing your code in this way, the main place you’ll run into issues with
this
is when handling DOM events (wherethis
is the element on which the event occurred). In that case, just be careful when using a callback within that event handler, that you’re not expectingthis
to still refer to the element (and useproxy
orthat
/self
if you need to refer to it).Finally, as demonstrated in the example, you can use
call
orapply
on a function to set itsthis
value. This isn’t often needed, but you may also want to know that you can useapply
to pass in an array of arguments to a function (e.g.console.log.apply(console, [1, 2, 3, 4])
). -
JavaScript: Global Variables
One of the biggest issues that you can run into with writing JavaScript is abusing the shared global namespace, and overwriting the variables of an unrelated script. Everything that you declare in JavaScript is available to every other script on the page, unless you wrap the declaration in a function. Also, if you use a variable without declaring it (even within a function), it will be declared for you in the global scope.
There are a couple of remedies we have against creating a global soup of conflicting variables. Knowing is half the battle, so step one is just keeping an eye out. Recognize when you’re creating a function or variable, and make sure that it’s not a global. The main technique for this is to declare everything inside a function. If you’re writing jQuery scripts, most of your script is probably being written inside the callback passed to
jQuery(document).ready()
, so you’re mostly safe already. If you’re not using a function already, you can wrap your code in an immediately invoked function expression.Step two is to catch when you miss declaring a variable. There are two ways to do this. The first is to turn on strict mode, so that browsers which support strict mode will throw an error when you try to access an undefined variable (instead of defining it as a global). In addition, you should be running your code through a linter (JSLint or JSHint) to catch issues like this.
So, now that you know how not to expose global variable, what about if you need to? If you need to write code in one script and reference it from another, you’ll need to expose a reference to that code somehow, right?
In this case, you should still probably be using all of the above techniques to keep the scope clean, but then explicitly expose anything that you need to. Again, if you’re using jQuery, this is fairly natural. Your script will take a reference to the global jQuery function, and you can add your script as a plugin to
jQuery.fn
. If you’re not exposing a jQuery plugin, you can take a reference to the globalwindow
object, and add your object explicitly. As an example:(function (window, $) { “use strict”; // turn on strict mode var x = 10; // x will not be accessible outside of this function y = x * 20; // y will be declared as a global variable, unless the browser supports strict mode $.fn.myPlugin = function (args) { ... }; // expose your plugin to the rest of the page window.textbox2_validate = function (sender, args) { ... }; // expose a standard function }(this, jQuery)) // immediately invoke this function expression, passing in this (a reference to the global scope) and jQuery
-
JavaScript Functions
When coming from a strongly-typed, object-oriented background, there may be some surprising elements when looking at how functions are used in JavaScript.
Functions in JavaScript don’t have any real concept of a signature, like they would in .NET. All functions return a value (which will be the value
undefined
if nothing is explicitly returned), so there are no void functions, and, while you can define named parameters for a function, callers of the function can provide more or less parameters than specified. If less parameters are provided, the other parameters will beundefined
, if more are provided, they are accessible via an implicit array-like variable namedarguments
. This also means that it’s not possible to have function overloads which have the same name and differ just by signature.There are also two different ways to create a function. The one that looks more like the object-oriented style is a function statement. This is a statement that starts with function, then gives the function name, parameter list, and body. One special characteristic of function statements is that they are defined throughout the entire scope of their containing function (i.e. they can be used before they are defined). They are said to be hoisted to the top of the containing function.
The other option is a function expression. This can look just like a function statement, but doesn’t require a function name, and is a value that can be assigned to a variable, passed to another function, or invoked. Let’s see some examples:
var myFunc = function (x) { if (typeof x === 'undefined') { return 'no args'; } else if (typeof x === 'function') { return x(); } else if (arguments.length === 1) { return 'x was ' + x; } else { return arguments.length - 1 + ' extra arguments provided'; } }, result1 = myFunc(), result2 = myFunc(function (y) { console.log(y); }), result3 = myFunc(statement), result4 = myFunc(1), result5 = myFunc({}, true, 'text'); console.log(result1, result2, result3, result4, result5); function statement(x, y, z) { console.log(x, y, z); }
So, we start here by defining a variable
myFunc
, whose value is a function expression. The function has one parameter,x
, and returns a string value indicating what was passed in. Forresult1
, we callmyFunc
without any arguments, and see thatx
isundefined
. Forresult2
andresult3
, we callmyFunc
with a function argument (forresult2
it’s a function expression, forresult3
it’s a reference to a function defined via a function statement), call that function, and return the result. Because both functions we pass in don’t return anything, the result isundefined
. Note that inresult3
, we were able to use statement before it was defined. Forresult4
, we callmyFunc
with one argument, and refer to the value via the parameterx
. Forresult5
, we callmyFunc
with more than one argument, and refer to thearguments
collection. -
Why isn’t My Scheduled Task Executing?
A quick diversion from the JavaScript Common Difficulties and Misconceptions series (I have another post queued up, just need to write the sample code), for an issue I just ran into.
Created a scheduled task for DNN, but it’s sitting in the scheduler queue, just getting more and more overdue. Why won’t my task run?
Somehow I missed the constructor from the tasks I copied. Your task needs a constructor that takes a
ScheduleHistoryItem
to run via the scheduler. -
Closure and Scope
The term closure refers to a function which references variables outside of its scope; the function is “closed over” those variables.
I’ll give an example of misusing closure in JavaScript and C#, then compare the solutions. This example is going to attempt the trivial task of outputting the digits 0 through 9.
JavaScript
for (x = 0; x < 10; x++) { setTimeout(function () { console.log(x); }, 1); }
C#
for (int x = 0; x < 10; x++) { setTimeout(() => Console.WriteLine(x) , 1); }
This assumes that a setTimeout method has been created for C#, just for the purposes of demonstrating this. In reality, you’ll run into this when you’re wiring up events with lambdas, iterating over LINQ queries, or using methods like List<T>’s ForEach.
The problem in these two examples is that the function passed to setTimeout is closed over the variable x; that is, we’re not referencing the value of x in the function, but the variable itself (similarly to a ref parameter in a C# method). By the time the function runs, x is 10, so the output to the console is actually 10 being output 10 times (see for yourself).
To fix this problem, we need to pass a new variable which always hold the correct value to each function. In C# this is pretty easy, because the for block provides scope.
for (int x = 0; x < 10; x++) { var closedX = x; setTimeout(() => Console.WriteLine(closedX) , 1); }
However, in JavaScript, only functions provide scope. If we tried this same technique in JavaScript, closedX would be the same variable in each run of the for loop (even though we’re using var to “declare” it). We need a new function to provide the scope to create separate variables for each run.
for (x = 0; x < 10; x++) { setTimeout((function (closedX) { return function () { console.log(closedX); } }(x)), 1); }
Here, we create a function which has one parameter, closedX, then immediately call it, passing in x. The parameter is a new variable for each run of the for loop because the function give it a scope, so it gives us the desired outcome.
Feel free to leave any questions in the comments, play with the example above on jsFiddle, and enjoy JavaScript to its fullest.
-
JavaScript: The Language
I gave my JavaScript language talk at the St. Louis .NET/DotNetNuke User Group last night. I’ve posted the presentation (with some updated, clarified example code) up on slideshare.
I woke up this morning thinking through all of the things that I could have explained better and how to more succinctly get the important points across. So, I’m now declaring (so that you, my loyal readership, can hold me to it) that I’m going to do a series on this blog focusing on illuminating common difficulties and misconceptions in JavaScript. Look forward to a discussion of closure sometime this week.
-
Escaping Commas in ReSharper Live Templates Comma-Delimited Macros
ReSharper is an irreplaceable tool in my coding arsenal. One of its myriad of amazingly useful tools is Live Templates, a much enhanced version of Visual Studio’s code snippets. When defining a live template you can specify a number of placeholders in the template, and choose from a variety of “macros” to determine how ReSharper fills that field in. We use this to automate putting the current year number and filename in the copyright header in new files, and to have ReSharper suggest appropriate types or variables names.
In a new template that I was writing, I wanted to be able to choose from a list of SQL Server types. Looking through the list of available macros, I was pleased to see that there was an option for a comma-delimited list of options. I started creating this list, and then came across decimal(18,2) as a type, and realized quickly that it was coming as two options in the list (decimal(18 and 2)), so I started looking for a way to escape that comma. After finding nothing in Google with a couple of searches, I tried a few guesses. It only took two guesses to find that you can use a backslash to escape the comma, ending up with decimal(18\,2).
Thanks, JetBrains, for ReSharper and its ability to do what I need it to do without even trying.